diff -urdBN emech-2.8.1/README.SERVICES emech-2.8.1-services/README.SERVICES
--- emech-2.8.1/README.SERVICES	Thu Jan  1 10:00:00 1970
+++ emech-2.8.1-services/README.SERVICES	Tue Dec 14 00:00:00 1999
@@ -0,0 +1,65 @@
+*** Please keep in mind this is BETA code.. ***
+
+Usage:
+
+-SERVICE <ADD|DEL> [channel] <to> <message>
+or
+-SERVICE LIST [<channel>|LOGIN]
+
+There are 2 facilities supported by this SERVICE command.. One is
+for sending requests upon logging into a server, and the other is
+for requesting channel-op status from another channel-op. Both can 
+have multiple requests specified, so authorizing to many different 
+services is possible.   
+
+With multiple requests upon logging in, there is no flood control,
+so if you have to many requests, you will most likely flood the bot
+off IRC before you even get to see it. Use your judgement.
+
+With the channel-op request, things are different. The bot will
+only send a request if it detects that the recipent of the message
+is present and opped. If there are multiple requests involved, the 
+bot will cycle the list, with one request about every 10 seconds,
+until it has been opped. 
+
+This semi-intellegent facility also makes it reasonably safe to gain 
+channel-ops from other standard bots, like eggdrop. (reason: if 
+somebody dupes a botnick, and gets opped as well, your channel 
+security is already screwed. ;) Again, use your judgement.
+
+Examples:
+~~~~~~~~~
+1) Sending a service request upon login:
+
+   -SERVICE ADD W@Channels2.Undernet.Org LOGIN #mychan mypass
+   -SERVICE ADD X@Channels.Undernet.Org LOGIN #myotherchan coolpass
+
+   Everytime it connects to a server, it will send
+   'LOGIN #mychan mypass' to 'W@Channels2.Undernet.Org' and
+   'LOGIN #myotherchan coolpass' to 'X@Channels.Undernet.Org'
+
+2) Sending a need ops request upon joining a channel:
+
+   -SERVICE ADD #mychannel W OP #mychannel $nick
+
+   This will send 'OP #mychannel <botname>' to W as soon as the
+   bot joins the channel #mychannel, on the condition that W is
+   actually *present* and *opped*.
+   The '$nick' is used to substitute the bots current nick 
+   at the time.    
+
+3) Displaying which services requests are set:
+
+   -SERVICE LIST 	     	(displays all services requests)
+   -SERVICE LIST LOGIN 		(displays LOGIN services requests)
+   -SERVICE LIST #mychannel     (displays #mychannel NEEDOP requests)
+
+4) Deleting a service request:
+
+   -SERVICE DEL W@Channels2.Undernet.Org LOGIN #mychan mypass
+
+   will delete the first service used in example 1.
+
+   -SERVICE DEL #mychannel W OP #mychannel $nick
+
+   will delete the service used in example 2.
\ No newline at end of file
diff -urdBN emech-2.8.1/src/cfgfile.c emech-2.8.1-services/src/cfgfile.c
--- emech-2.8.1/src/cfgfile.c	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/cfgfile.c	Wed Apr  4 17:10:46 2001
@@ -737,6 +737,33 @@
 
 #endif /* ALIASES */
 
+void cfg_service(char *args)
+{
+	char 	 	*to,*msg;
+	aService 	**servicesptr;
+	
+	to = get_token(&args,TOC);
+	msg = args;
+	
+        if (!msg || !*msg) return;
+        
+	if (cfgChan) 
+	    servicesptr = &(cfgChan->ServicesList);
+	else 
+	    servicesptr = &(current->ServicesList);
+	
+	if (find_service(servicesptr,to,msg))
+	{
+		printf("init: Service %s already exists (ignored)\n",to);
+		return;
+	}
+	
+	if (!add_service(servicesptr,to,msg))
+		printf("init: Failed to add service %s",to);
+	
+	return;
+}
+
 typedef struct CommandStruct
 {
 	char	*name;
@@ -788,6 +815,7 @@
 #ifdef ALIASES
 { "ALIAS",	cfg_alias,	0	},
 #endif /* ALIASES */
+{ "SERVICE",    cfg_service,    0       },
 { NULL,		NULL,		0	}
 };
 
diff -urdBN emech-2.8.1/src/channel.c emech-2.8.1-services/src/channel.c
--- emech-2.8.1/src/channel.c	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/channel.c	Wed Apr  4 17:11:02 2001
@@ -109,6 +109,43 @@
 	}
 }
 
+void needop_via_msg(char *channel)
+{
+	aChan 		*Chan;
+	aService	*tmpserviceptr;
+	int		cycled;
+	
+	if ((Chan = find_channel(channel,CH_ACTIVE)) == NULL)
+		return;
+	
+	if (Chan->ServicesList == NULL) 
+		return;
+	
+	if (is_opped(current->nick,channel)) 
+		return;
+	
+	tmpserviceptr = Chan->nextservice;
+	
+	cycled = 0;
+	while (!cycled)
+	{
+		if (tmpserviceptr == NULL) 
+			tmpserviceptr = Chan->ServicesList;
+
+		if (is_opped(tmpserviceptr->to,channel))
+		{
+			send_to_server("PRIVMSG %s :%s",tmpserviceptr->to,
+				subststrvar(tmpserviceptr->msg));
+			Chan->nextservice = tmpserviceptr->next;
+			return;
+		}	
+
+		tmpserviceptr = tmpserviceptr->next;
+		if (tmpserviceptr == Chan->nextservice)
+			cycled = 1;
+	}
+}
+
 void check_shit(void)
 {
 	aChanUser *User;
diff -urdBN emech-2.8.1/src/combot.c emech-2.8.1-services/src/combot.c
--- emech-2.8.1/src/combot.c	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/combot.c	Wed Apr  4 17:11:43 2001
@@ -31,6 +31,8 @@
 #ifdef LINKING
 	aLinkConf *lc;
 #endif /* LINKING */
+	aService *tmpserviceptr;
+
 	VarStruct *Vars,*CV;
 	aServer	*sp;
 	aChan	*Chan;
@@ -105,6 +107,13 @@
 		tofile(sf,"ircname %s\n",bot->ircname);
 		if (bot->modes[0])
 			tofile(sf,"modes %s\n",bot->modes);
+		tmpserviceptr = bot->ServicesList;
+		while (tmpserviceptr)
+		{
+			tofile(sf, "service %s %s\n",
+				tmpserviceptr->to,tmpserviceptr->msg);
+			tmpserviceptr = tmpserviceptr->next;
+		}
 		if (bot->cmdchar != DEFAULTCMDCHAR)
 			tofile(sf,"cmdchar %c\n",bot->cmdchar);
 		tofile(sf,"userfile %s\n\n",bot->Userfile);
@@ -147,6 +156,13 @@
 			if ((Chan->active == CH_OLD) && (!Chan->rejoin))
 				continue;
 			tofile(sf,"channel %s\n",Chan->name,(Chan->key && *Chan->key) ? Chan->key : "");
+			tmpserviceptr = Chan->ServicesList;
+			while (tmpserviceptr)
+			{
+				tofile(sf, "service %s %s\n",
+					tmpserviceptr->to,tmpserviceptr->msg);
+				tmpserviceptr = tmpserviceptr->next;
+			}
 			Vars = Chan->Vars;
 			for(j=0;VarName[j].name;j++)
 			{
@@ -213,6 +229,58 @@
  *  Bot adding and killing
  */
 
+int add_service(aService **l_list, char *to, char *msg)
+{
+	aService 	*tmpserviceptr;
+
+	if ((tmpserviceptr = (aService*)MyMalloc(sizeof(aService))) == NULL)
+		return(FALSE);
+
+	tmpserviceptr->next = *l_list;
+	mstrcpy(&tmpserviceptr->to,to);
+	mstrcpy(&tmpserviceptr->msg,msg);
+	*l_list = tmpserviceptr;	
+	return(TRUE);
+}
+
+int delete_service(aService **l_list, aService *service)
+{
+	aService 	**old;
+	
+	for(old=l_list;*old;old=&(**old).next)
+		if (*old == service)
+		{
+			*old = service->next;
+			MyFree(&service->to);
+			MyFree(&service->msg);
+			MyFree((char **)&service);
+			return(TRUE);
+		}
+	return(FALSE);
+}	
+
+aService *find_service(aService **l_list, char *to, char *msg)
+{
+	aService 	*tmpserviceptr;
+	
+	tmpserviceptr = *l_list;
+	
+	while (tmpserviceptr)
+	{
+		if (!strcasecmp(to,tmpserviceptr->to))
+			if (!strcasecmp(msg,tmpserviceptr->msg))
+				return(tmpserviceptr);
+		tmpserviceptr = tmpserviceptr->next;
+	}
+	return(NULL);			
+}	
+
+void delete_all_services(aService *ServicesList)
+{
+	while (ServicesList)
+		delete_service(&ServicesList,ServicesList);
+}
+
 aBot *add_bot(char *nick)
 {
 #ifdef MULTI
@@ -469,6 +537,7 @@
 	delete_seen();
 	MyFree(&current->seenfile); 
 #endif /* SEEN */
+	delete_all_services(current->ServicesList);
 
 #ifdef DEBUG
 	debug("(signoff) Removing userlist...\n");
@@ -770,6 +839,12 @@
 				if (!Chan->bot_is_op && Chan->active == CH_ACTIVE)
 					needop(Chan->name);
 #endif /* LINKING */
+				if ((now - Chan->lastservice) > 10)
+				{
+					if ((usermode(Chan->name,current->nick) & CU_CHANOP) == 0)
+						needop_via_msg(Chan->name);
+					Chan->lastservice = now;
+				}
 			}
 			if (!x)
 			{
diff -urdBN emech-2.8.1/src/commands.c emech-2.8.1-services/src/commands.c
--- emech-2.8.1/src/commands.c	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/commands.c	Wed Apr  4 17:11:59 2001
@@ -20,6 +20,186 @@
 */
 #include "config.h"
 
+void do_service(char *from, char *to, char *rest, int cmdlevel)
+{
+	char 	   	*scmd,*schan,*sto,*smsg;
+	aChan	 	*tmpchanptr;
+	aService 	**servicesptr,*tmpserviceptr;
+	
+	scmd = get_token(&rest," ");
+	
+	if (!scmd || !*scmd)
+		goto usage;
+	
+	if (!strcasecmp(scmd,"ADD"))
+	{
+		sto = get_token(&rest," ");
+		
+		if (!sto || !*sto)
+			goto usage;
+			
+		if (ischannel(sto))
+		{
+			schan = sto;
+			sto = get_token(&rest," ");
+			
+			if ((tmpchanptr = find_channel(schan,CH_ANY)) == NULL)
+			{
+				send_to_user(from,"I have no knowledge of channel %s",schan);
+				return;
+			}		
+		
+			servicesptr = &tmpchanptr->ServicesList;
+		}
+		else
+		{
+			servicesptr = &current->ServicesList;
+		}
+		
+		smsg = rest;
+		
+		if (!smsg || !*smsg)
+			goto usage;
+			
+		if (find_service(servicesptr,sto,smsg))
+		{
+			send_to_user(from,"Service definition already exists.");
+			return;
+		}
+		
+		if (!add_service(servicesptr,sto,smsg))
+			send_to_user(from,"Error adding service.");
+		else
+			send_to_user(from,"Service added successfully.");
+		
+		return;
+	}
+	else
+	if (!strcasecmp(scmd,"DEL"))
+	{
+		sto = get_token(&rest," ");
+		
+		if (!sto || !*sto)
+			goto usage;
+			
+		if (ischannel(sto))
+		{
+			schan = sto;
+			sto = get_token(&rest," ");
+			
+			if ((tmpchanptr = find_channel(schan,CH_ANY)) == NULL)
+			{
+				send_to_user(from,"I have no knowledge of channel %s",schan);
+				return;
+			}		
+		
+			servicesptr = &tmpchanptr->ServicesList;
+		}
+		else
+		{
+			servicesptr = &current->ServicesList;
+		}
+		
+		smsg = rest;
+		
+		if (!smsg || !*smsg)
+			goto usage;
+			
+		if ((tmpserviceptr = find_service(servicesptr,sto,smsg)) == NULL)
+		{
+			send_to_user(from,"Service definition specified doesn't exist!");
+			return;
+		}
+
+		if (!delete_service(servicesptr,tmpserviceptr))
+			send_to_user(from,"Error deleting service.");
+		else
+			send_to_user(from,"Service deleted.");
+		
+		return;
+	}
+	else
+	if (!strcasecmp(scmd,"LIST"))
+	{
+		sto = get_token(&rest," ");
+		
+		if (!sto || !*sto)
+		{
+			send_to_user(from,"Services List:");
+			send_to_user(from,"  LOGIN:");
+			
+			tmpserviceptr = current->ServicesList;
+			while (tmpserviceptr)
+			{
+				send_to_user(from,"    TO: %s, MSG: %s",
+					tmpserviceptr->to,tmpserviceptr->msg);
+				tmpserviceptr = tmpserviceptr->next;
+			}
+			
+			send_to_user(from,"");
+			
+			tmpchanptr = current->Channels;
+						
+			while (tmpchanptr)
+			{
+				send_to_user(from,"  NEEDOP for %s:",tmpchanptr->name);
+				
+				tmpserviceptr = tmpchanptr->ServicesList;
+				
+				while (tmpserviceptr)
+				{
+					send_to_user(from,"    TO: %s, MSG: %s",
+						tmpserviceptr->to,tmpserviceptr->msg);
+				
+					tmpserviceptr = tmpserviceptr->next;
+				}
+			
+				tmpchanptr = tmpchanptr->next;
+			}
+		}	
+		else
+		if (!strcasecmp(sto,"LOGIN"))
+		{
+			send_to_user(from,"Services List:");
+			send_to_user(from,"  LOGIN:");
+			
+			tmpserviceptr = current->ServicesList;
+			while (tmpserviceptr)
+			{
+				send_to_user(from,"    TO: %s, MSG: %s",
+					tmpserviceptr->to,tmpserviceptr->msg);
+				tmpserviceptr = tmpserviceptr->next;
+			}
+		}
+		else
+		if (ischannel(sto))
+		{
+			schan = sto;
+			
+			if ((tmpchanptr = find_channel(schan,CH_ANY)) == NULL)
+			{
+				send_to_user(from,"I have no knowledge of channel %s",schan);
+				return;
+			}		
+
+			send_to_user(from,"Services List:");
+			send_to_user(from,"  NEEDOP for %s:",tmpchanptr->name);
+			
+			tmpserviceptr = tmpchanptr->ServicesList;
+			while (tmpserviceptr)
+			{
+				send_to_user(from,"    TO: %s, MSG: %s",
+					tmpserviceptr->to,tmpserviceptr->msg);
+				tmpserviceptr = tmpserviceptr->next;
+			}
+		}
+		return;
+	}
+
+usage:
+	usage(from,C_SERVICE);
+}
+
 void do_stats(char *from, char *to, char *rest, int cmdlevel)
 {
 	aStrp	*ump;
diff -urdBNU 1 emech-2.8.1/src/gencmd.c emech-2.8.1-services/src/gencmd.c
--- emech-2.8.1/src/gencmd.c	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/gencmd.c	Wed Apr  4 18:45:55 2001
@@ -176,2 +176,3 @@
 
+	{ 0, "SERVICE",         "do_service",           100     | CC    | PASS  | DCC 		},
 	{ 0, "CORE",		"do_core",		100	| CC	| PASS	| DCC		},
diff -urdBNU 1 emech-2.8.1/src/h.h emech-2.8.1-services/src/h.h
--- emech-2.8.1/src/h.h	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/h.h	Wed Apr  4 18:45:55 2001
@@ -123,2 +123,3 @@
 aServer *add_server(char *, int, char *);
+aService *find_service(aService **, char *, char *);
 aTime *find_time(aTime **, char *);
@@ -160,2 +161,3 @@
 char *sockread(int, char *, char *);
+char *subststrvar(char *);
 char *terminate(char *, char *);
@@ -167,2 +169,3 @@
 int *Atoi(char *);
+int add_service(aService **, char *, char *);
 int capslevel(char *);
@@ -179,2 +182,3 @@
 int remove_chanuser(aChan *, char *);
+int delete_service(aService **, aService *);
 int find_var_name(char *, int);
@@ -260,2 +264,3 @@
 void delete_all_channels(void);
+void delete_all_services(aService *);
 void delete_bans(aBan **);
@@ -348,2 +353,3 @@
 void do_server(char *, char *, char *, int);
+void do_service(char *, char *, char *, int);
 void do_serverlist(char *, char *, char *, int);
@@ -394,2 +400,3 @@
 void needop(char *);
+void needop_via_msg(char *);
 void no_info(char *, char *);
diff -urdBN emech-2.8.1/src/parse.c emech-2.8.1-services/src/parse.c
--- emech-2.8.1/src/parse.c	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/parse.c	Wed Apr  4 17:13:48 2001
@@ -355,6 +355,7 @@
 
 void parse_001(char *from, char *rest)
 {
+	aService *tmpserviceptr;
 	aServer *sp;
 	char	*newnick;
 
@@ -374,6 +375,13 @@
 			strncpy(sp->realname,from,NAMELEN-1);
 			sp->realname[NAMELEN-1] = 0;
 		}
+	}
+	tmpserviceptr = current->ServicesList;
+	while (tmpserviceptr)
+	{
+		send_to_server("PRIVMSG %s :%s",
+			tmpserviceptr->to,subststrvar(tmpserviceptr->msg));	
+		tmpserviceptr = tmpserviceptr->next;
 	}
 }
 
diff -urdBN emech-2.8.1/src/structs.h emech-2.8.1-services/src/structs.h
--- emech-2.8.1/src/structs.h	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/structs.h	Wed Apr  4 17:14:47 2001
@@ -123,6 +123,13 @@
 
 } aUser;
 
+typedef struct aService
+{
+	char 	*to;
+	char 	*msg;
+	struct  aService *next;
+} aService;
+
 typedef struct aBan
 {
 	struct		aBan *next;
@@ -186,6 +193,9 @@
 			pipeuser:1,		/* channel was JOIN'd by pipeuser	*/
 #endif /* PIPEUSER */
 			rejoin:1;		/* trying to rejoin it?			*/
+	aService        *ServicesList;  	/* PRIVMSGs to send for a NEEDOP        */
+	aService        *nextservice;   	/* next service in list to try          */
+        time_t		lastservice;    	/* last time a service PRIVMSG was sent */
 
 } aChan;
 
@@ -310,6 +320,7 @@
 #endif /* SEEN */
 
 	char		*lastcmds[LASTCMDSIZE];
+	aService        *ServicesList;  
 
 	time_t		lastreset;		/* last time bot was reset		*/
 	time_t		lastping;		/* to check activity server		*/
diff -urdBNU 1 emech-2.8.1/src/usage.h emech-2.8.1-services/src/usage.h
--- emech-2.8.1/src/usage.h	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/usage.h	Wed Apr  4 18:45:55 2001
@@ -114,2 +114,3 @@
 #endif
+{ S_SERVICE,    "@ <add|del> [channel] <to> <message>"                                  },
 { S_SERVER,	"@ <servername> [port] [login] [ircname]"				},
diff -urdBN emech-2.8.1/src/xmech.c emech-2.8.1-services/src/xmech.c
--- emech-2.8.1/src/xmech.c	Mon Apr  2 04:26:12 2001
+++ emech-2.8.1-services/src/xmech.c	Wed Apr  4 17:15:19 2001
@@ -264,6 +264,37 @@
 	sendprivmsg(chan,"%s",res);
 }
 
+char *subststrvar(char *line)
+{
+	static char res[MAXLEN];
+	char *rs;
+	
+	memset(res,0,sizeof(res));
+	rs = res;
+	
+	while(*line)
+	{
+		if (*line == '$')
+		{
+			if (!strncasecmp(line,"$nick",5))
+			{
+				strcpy(rs,current->nick);
+				while (*rs) rs++;
+				line += 5;
+			}
+			else					
+			{
+				*(rs)++ = *(line)++;
+			}
+		}
+		else
+		{
+			*(rs)++ = *(line)++;
+		}
+	}
+	return(res);
+}
+
 void do_pickup(char *from, char *to, char *rest, int cmdlevel)
 {
 	char	*chan,*nick,*pickup;
