Ejemplo n.º 1
0
static void sttSendPrivateMessage( JABBER_LIST_ITEM* item, const TCHAR* nick )
{
	TCHAR szFullJid[ 256 ];
	mir_sntprintf( szFullJid, SIZEOF(szFullJid), _T("%s/%s"), item->jid, nick );
	HANDLE hContact = JabberDBCreateContact( szFullJid, NULL, TRUE, FALSE );
	if ( hContact != NULL ) {
		for ( int i=0; i < item->resourceCount; i++ ) {
			if ( _tcsicmp( item->resource[i].resourceName, nick ) == 0 ) {
				JSetWord( hContact, "Status", item->resource[i].status );
				break;
		}	}

		DBWriteContactSettingByte( hContact, "CList", "Hidden", 1 );
		JSetStringT( hContact, "Nick", nick );
		DBWriteContactSettingDword( hContact, "Ignore", "Mask1", 0 );
		JCallService( MS_MSG_SENDMESSAGE, ( WPARAM )hContact, 0 );
}	}
Ejemplo n.º 2
0
void JabberDBAddAuthRequest( TCHAR* jid, TCHAR* nick )
{
	HANDLE hContact = JabberDBCreateContact( jid, NULL, FALSE, TRUE );
	JDeleteSetting( hContact, "Hidden" );
	//JSetStringT( hContact, "Nick", nick );

	#if defined( _UNICODE )
		char* szJid = u2a( jid );
		char* szNick = u2a( nick );
	#else
		char* szJid = jid;
		char* szNick = nick;
	#endif

	//blob is: uin( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), first( ASCIIZ ), last( ASCIIZ ), email( ASCIIZ ), reason( ASCIIZ )
	//blob is: 0( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ ), email( ASCIIZ ), ""( ASCIIZ )
	DBEVENTINFO dbei = {0};
	dbei.cbSize = sizeof( DBEVENTINFO );
	dbei.szModule = jabberProtoName;
	dbei.timestamp = ( DWORD )time( NULL );
	dbei.flags = 0;
	dbei.eventType = EVENTTYPE_AUTHREQUEST;
	dbei.cbBlob = sizeof( DWORD )+ sizeof( HANDLE ) + strlen( szNick ) + strlen( szJid ) + 5;
	PBYTE pCurBlob = dbei.pBlob = ( PBYTE ) mir_alloc( dbei.cbBlob );
	*(( PDWORD ) pCurBlob ) = 0; pCurBlob += sizeof( DWORD );
	*(( PHANDLE ) pCurBlob ) = hContact; pCurBlob += sizeof( HANDLE );
	strcpy(( char* )pCurBlob, szNick ); pCurBlob += strlen( szNick )+1;
	*pCurBlob = '\0'; pCurBlob++;		//firstName
	*pCurBlob = '\0'; pCurBlob++;		//lastName
	strcpy(( char* )pCurBlob, szJid ); pCurBlob += strlen( szJid )+1;
	*pCurBlob = '\0';					//reason

	JCallService( MS_DB_EVENT_ADD, ( WPARAM ) ( HANDLE ) NULL, ( LPARAM )&dbei );
	JabberLog( "Setup DBAUTHREQUEST with nick='" TCHAR_STR_PARAM "' jid='" TCHAR_STR_PARAM "'", szNick, szJid );

	#if defined( _UNICODE )
		mir_free( szJid );
		mir_free( szNick );
	#endif
}
Ejemplo n.º 3
0
void JabberIqResultGetRoster(XmlNode *iqNode, void *userdata)
{
	//struct ThreadData *info = (struct ThreadData *) userdata;
	XmlNode *queryNode;
	char *type;
	char *str;

	// RECVED: roster information
	// ACTION: populate LIST_ROSTER and create contact for any new rosters
	JabberLog("<iq/> iqIdGetRoster");
	if ((type=JabberXmlGetAttrValue(iqNode, "type")) == NULL) return;
	if ((queryNode=JabberXmlGetChild(iqNode, "query")) == NULL) return;

	if (!strcmp(type, "result")) {
		str = JabberXmlGetAttrValue(queryNode, "xmlns");
		if (str!=NULL && !strcmp(str, "jabber:iq:roster")) {
			DBVARIANT dbv;
			XmlNode *itemNode, *groupNode;
			JABBER_SUBSCRIPTION sub;
			JABBER_LIST_ITEM *item;
			HANDLE hContact;
			char *jid, *name, *nick;
			int i, oldStatus;

			for (i=0; i<queryNode->numChild; i++) {
				itemNode = queryNode->child[i];
				if (!strcmp(itemNode->name, "item")) {
					str = JabberXmlGetAttrValue(itemNode, "subscription");
					if (str==NULL) sub = SUB_NONE;
					else if (!strcmp(str, "both")) sub = SUB_BOTH;
					else if (!strcmp(str, "to")) sub = SUB_TO;
					else if (!strcmp(str, "from")) sub = SUB_FROM;
					else sub = SUB_NONE;
					//if (str!=NULL && (!strcmp(str, "to") || !strcmp(str, "both"))) {
					if ((jid=JabberXmlGetAttrValue(itemNode, "jid")) != NULL) {
						if ((name=JabberXmlGetAttrValue(itemNode, "name")) != NULL) {
							nick = JabberTextDecode(name);
						} else {
							nick = JabberLocalNickFromJID(jid);
						}
						if (nick != NULL) {
							item = JabberListAdd(LIST_ROSTER, jid);
							if (item->nick) free(item->nick);
							item->nick = nick;
							item->subscription = sub;
							if ((hContact=JabberHContactFromJID(jid)) == NULL) {
								// Received roster has a new JID.
								// Add the jid (with empty resource) to Miranda contact list.
								hContact = JabberDBCreateContact(jid, nick, FALSE, TRUE);
							}
							DBWriteContactSettingString(hContact, "CList", "MyHandle", nick);
							if (item->group) free(item->group);
							if ((groupNode=JabberXmlGetChild(itemNode, "group"))!=NULL && groupNode->text!=NULL) {
								item->group = JabberTextDecode(groupNode->text);
								JabberContactListCreateGroup(item->group);
								// Don't set group again if already correct, or Miranda may show wrong group count in some case
								if (!DBGetContactSetting(hContact, "CList", "Group", &dbv)) {
									if (strcmp(dbv.pszVal, item->group))
										DBWriteContactSettingString(hContact, "CList", "Group", item->group);
									DBFreeVariant(&dbv);
								}
								else
									DBWriteContactSettingString(hContact, "CList", "Group", item->group);
							}
							else {
								item->group = NULL;
								DBDeleteContactSetting(hContact, "CList", "Group");
							}
						}
					}
				}
			}
			// Delete orphaned contacts (if roster sync is enabled)
			if (DBGetContactSettingByte(NULL, jabberProtoName, "RosterSync", FALSE) == TRUE) {
				HANDLE *list;
				int listSize, listAllocSize;

				listSize = listAllocSize = 0;
				list = NULL;
				hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
				while (hContact != NULL) {
					str = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
					if(str!=NULL && !strcmp(str, jabberProtoName)) {
						if (!DBGetContactSetting(hContact, jabberProtoName, "jid", &dbv)) {
							if (!JabberListExist(LIST_ROSTER, dbv.pszVal)) {
								JabberLog("Syncing roster: preparing to delete %s (hContact=0x%x)", dbv.pszVal, hContact);
								if (listSize >= listAllocSize) {
									listAllocSize = listSize + 100;
									if ((list=(HANDLE *) realloc(list, listAllocSize)) == NULL) {
										listSize = 0;
										break;
									}
								}
								list[listSize++] = hContact;
							}
							DBFreeVariant(&dbv);
						}
					}
					hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
				}
				for (i=0; i<listSize; i++) {
					JabberLog("Syncing roster: deleting 0x%x", list[i]);
					CallService(MS_DB_CONTACT_DELETE, (WPARAM) list[i], 0);
				}
				if (list != NULL)
					free(list);
			}
			///////////////////////////////////////
			{
				CLISTMENUITEM clmi;
				memset(&clmi, 0, sizeof(CLISTMENUITEM));
				clmi.cbSize = sizeof(CLISTMENUITEM);
				clmi.flags = CMIM_FLAGS;
				CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuMUC, (LPARAM) &clmi);
				CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuChats, (LPARAM) &clmi);
			}

			jabberOnline = TRUE;
			JabberLog("Status changed via THREADSTART");
			oldStatus = jabberStatus;
			switch (jabberDesiredStatus) {
			case ID_STATUS_ONLINE:
			case ID_STATUS_NA:
			case ID_STATUS_FREECHAT:
			case ID_STATUS_INVISIBLE:
				jabberStatus = jabberDesiredStatus;
				break;
			case ID_STATUS_AWAY:
			case ID_STATUS_ONTHEPHONE:
			case ID_STATUS_OUTTOLUNCH:
				jabberStatus = ID_STATUS_AWAY;
				break;
			case ID_STATUS_DND:
			case ID_STATUS_OCCUPIED:
				jabberStatus = ID_STATUS_DND;
				break;
			}
			JabberSendPresence(jabberStatus);
			ProtoBroadcastAck(jabberProtoName, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, jabberStatus);
			//////////////////////////////////
		}
	}
}
Ejemplo n.º 4
0
void TlenProcessPresence(XmlNode *node, TlenProtocol *proto)
{
	HANDLE hContact;
	XmlNode *showNode, *statusNode;
	JABBER_LIST_ITEM *item;
	char *from, *type, *nick, *show;
	int status, laststatus = ID_STATUS_OFFLINE;
	char *p;

	if ((from=JabberXmlGetAttrValue(node, "from")) != NULL) {
		if (JabberListExist(proto, LIST_CHATROOM, from)); //JabberGroupchatProcessPresence(node, userdata);

		else {
			type = JabberXmlGetAttrValue(node, "type");
			item = JabberListGetItemPtr(proto, LIST_ROSTER, from);
			if (item != NULL) {
				if (proto->tlenOptions.enableAvatars) {
					TlenProcessPresenceAvatar(proto, node, item);
				}
			}
			if (type==NULL || (!strcmp(type, "available"))) {
				if ((nick=JabberLocalNickFromJID(from)) != NULL) {
					if ((hContact=JabberHContactFromJID(proto, from)) == NULL)
						hContact = JabberDBCreateContact(proto, from, nick, FALSE);
					if (!JabberListExist(proto, LIST_ROSTER, from)) {
						JabberLog(proto, "Receive presence online from %s (who is not in my roster)", from);
						JabberListAdd(proto, LIST_ROSTER, from);
					}
					status = ID_STATUS_ONLINE;
					if ((showNode=JabberXmlGetChild(node, "show")) != NULL) {
						if ((show=showNode->text) != NULL) {
							if (!strcmp(show, "away")) status = ID_STATUS_AWAY;
							else if (!strcmp(show, "xa")) status = ID_STATUS_NA;
							else if (!strcmp(show, "dnd")) status = ID_STATUS_DND;
							else if (!strcmp(show, "chat")) status = ID_STATUS_FREECHAT;
							else if (!strcmp(show, "unavailable")) {
								// Always show invisible (on old Tlen client) as invisible (not offline)
								status = ID_STATUS_OFFLINE;
							}
						}
					}

					statusNode = JabberXmlGetChild(node, "status");
					if (statusNode)
						p = JabberTextDecode(statusNode->text);
					else
						p = NULL;
					JabberListAddResource(proto, LIST_ROSTER, from, status, statusNode?p:NULL);
					if (p) {
						DBWriteContactSettingString(hContact, "CList", "StatusMsg", p);
						mir_free(p);
					} else {
						DBDeleteContactSetting(hContact, "CList", "StatusMsg");
					}
					// Determine status to show for the contact and request version information
					if (item != NULL) {
						laststatus = item->status;
						item->status = status;
					}
					if (strchr(from, '@')!=NULL || DBGetContactSettingByte(NULL, proto->iface.m_szModuleName, "ShowTransport", TRUE)==TRUE) {
						if (DBGetContactSettingWord(hContact, proto->iface.m_szModuleName, "Status", ID_STATUS_OFFLINE) != status)
							DBWriteContactSettingWord(hContact, proto->iface.m_szModuleName, "Status", (WORD) status);
					}
					if (item != NULL) {
						if (!item->infoRequested) {
							int iqId = JabberSerialNext(proto);
							item->infoRequested = TRUE;
							JabberSend( proto, "<iq type='get' id='"JABBER_IQID"%d'><query xmlns='jabber:iq:info' to='%s'></query></iq>", iqId, from);
						}
						if (proto->tlenOptions.enableVersion && !item->versionRequested) {
							item->versionRequested = TRUE;
							if (proto->iface.m_iStatus != ID_STATUS_INVISIBLE) {
								JabberSend( proto, "<message to='%s' type='iq'><iq type='get'><query xmlns='jabber:iq:version'/></iq></message>", from );
							}
						}
					}
					JabberLog(proto, "%s (%s) online, set contact status to %d", nick, from, status);
					mir_free(nick);
				}
			}
			else if (!strcmp(type, "unavailable")) {
				if (!JabberListExist(proto, LIST_ROSTER, from)) {
					JabberLog(proto, "Receive presence offline from %s (who is not in my roster)", from);
					JabberListAdd(proto, LIST_ROSTER, from);
				}
				else {
					JabberListRemoveResource(proto, LIST_ROSTER, from);
				}
				status = ID_STATUS_OFFLINE;
				statusNode = JabberXmlGetChild(node, "status");
				if (statusNode) {
					if (proto->tlenOptions.offlineAsInvisible) {
						status = ID_STATUS_INVISIBLE;
					}
					p = JabberTextDecode(statusNode->text);
					JabberListAddResource(proto, LIST_ROSTER, from, status, p);
					if ((hContact=JabberHContactFromJID(proto, from)) != NULL) {
						if (p) {
							DBWriteContactSettingString(hContact, "CList", "StatusMsg", p);
						} else {
							DBDeleteContactSetting(hContact, "CList", "StatusMsg");
						}
					}
					if (p) mir_free(p);
				}
				if ((item=JabberListGetItemPtr(proto, LIST_ROSTER, from)) != NULL) {
					// Determine status to show for the contact based on the remaining resources
					item->status = status;
					item->versionRequested = FALSE;
					item->infoRequested = FALSE;
				}
				if ((hContact=JabberHContactFromJID(proto, from)) != NULL) {
					if (strchr(from, '@')!=NULL || DBGetContactSettingByte(NULL, proto->iface.m_szModuleName, "ShowTransport", TRUE)==TRUE) {
						if (DBGetContactSettingWord(hContact, proto->iface.m_szModuleName, "Status", ID_STATUS_OFFLINE) != status)
							DBWriteContactSettingWord(hContact, proto->iface.m_szModuleName, "Status", (WORD) status);
					}
					if (item != NULL && item->isTyping) {
						item->isTyping = FALSE;
						CallService(MS_PROTO_CONTACTISTYPING, (WPARAM) hContact, PROTOTYPE_CONTACTTYPING_OFF);
					}
					JabberLog(proto, "%s offline, set contact status to %d", from, status);
				}
			}
			else if (!strcmp(type, "subscribe")) {
				if (strchr(from, '@') == NULL) {
					// automatically send authorization allowed to agent/transport
					JabberSend(proto, "<presence to='%s' type='subscribed'/>", from);
				}
				else if ((nick=JabberNickFromJID(from)) != NULL) {
					JabberLog(proto, "%s (%s) requests authorization", nick, from);
					JabberDBAddAuthRequest(proto, from, nick);
					mir_free(nick);
				}
			}
			else if (!strcmp(type, "subscribed")) {
				if ((item=JabberListGetItemPtr(proto, LIST_ROSTER, from)) != NULL) {
					if (item->subscription == SUB_FROM) item->subscription = SUB_BOTH;
					else if (item->subscription == SUB_NONE) {
						item->subscription = SUB_TO;
					}
				}
			}
		}
	}
}
Ejemplo n.º 5
0
static int TlenMUCHandleEvent(WPARAM wParam, LPARAM lParam)
{
	HANDLE hContact;
	int id;
	MUCCEVENT *mucce=(MUCCEVENT *) lParam;
	if (!strcmp(mucce->pszModule, jabberProtoName)) {
		JabberLog("ME ! %d", mucce->iType);
		switch (mucce->iType) {
			case MUCC_EVENT_INVITE:
				TlenMUCSendInvitation(mucce->pszID, mucce->pszNick);
				break;
			case MUCC_EVENT_MESSAGE:
				TlenMUCSendMessage(mucce);
				break;
			case MUCC_EVENT_TOPIC:
				TlenMUCSendTopic(mucce);
				break;
			case MUCC_EVENT_LEAVE:
				TlenMUCSendPresence(mucce->pszID, NULL, ID_STATUS_OFFLINE);
				break;
			case MUCC_EVENT_QUERY_GROUPS:
				TlenMUCSendQuery(1, mucce->pszID, 0);
				break;
			case MUCC_EVENT_QUERY_ROOMS:
				TlenMUCSendQuery(2, mucce->pszID, mucce->dwData);
				break;
			case MUCC_EVENT_QUERY_SEARCH:
				TlenMUCSendQuery(3, mucce->pszName, 0);
				break;
			case MUCC_EVENT_QUERY_USERS:
				JabberLog("query3=%d",mucce->dwFlags);
				switch (mucce->dwFlags) {
				case MUCC_EF_USER_OWNER:
					id = 1;
					break;
				case MUCC_EF_USER_ADMIN:
					id = 2;
					break;
				case MUCC_EF_USER_MEMBER:
					id = 3;
					break;
				case MUCC_EF_USER_BANNED:
					id = 4;
					break;
				case MUCC_EF_USER_MODERATOR:
					id = 6;
					break;
				default:
					id = 0;
				}
				TlenMUCSendQuery(4, mucce->pszID, id);
				break;
			case MUCC_EVENT_REGISTER_NICK:
				TlenMUCSendQuery(6, mucce->pszNick, 0);
				break;
			case MUCC_EVENT_REMOVE_NICK:
				TlenMUCSendQuery(6, mucce->pszNick, 1);
				break;
			case MUCC_EVENT_REGISTER_ROOM:
				id = JabberSerialNext();
				if (jabberOnline) {
					if (mucce->pszNick!=NULL) {
						JabberSend(jabberThreadInfo->s, "<p to='c' tp='c' id='"JABBER_IQID"%d' x='%d' n='%s' p='%s' nick='%s'/>", id, mucce->dwFlags | 0x10, mucce->pszName, mucce->pszID);
					} else {
						JabberSend(jabberThreadInfo->s, "<p to='c' tp='c' id='"JABBER_IQID"%d' x='%d' n='%s' p='%s'/>", id, mucce->dwFlags | 0x10, mucce->pszName, mucce->pszID);
					}
				}
				break;
			case MUCC_EVENT_REMOVE_ROOM:
				if (jabberOnline) {
					JabberSend(jabberThreadInfo->s, "<p to='%s' type='d'/>", mucce->pszID);
					JabberListRemove(LIST_CHATROOM, mucce->pszID);
				//	TlenMUCSendPresence(mucce->pszID, NULL, ID_STATUS_OFFLINE);
				}
				break;
			case MUCC_EVENT_KICK_BAN:
				if (jabberOnline) {
					char *nick;
					nick = JabberResourceFromJID(mucce->pszUID);
					if (!isSelf(mucce->pszID, nick)) {
						char *reason = JabberTextEncode(mucce->pszText);
						JabberSend(jabberThreadInfo->s, "<p to='%s'><x><i i='%s' a='4' ex='%d' rs='%s'/></x></p>", mucce->pszID, nick, mucce->dwData, reason);
						mir_free(reason);
					}
					mir_free(nick);
				}
				break;
			case MUCC_EVENT_UNBAN:
				if (jabberOnline) {
					char *nick;
					nick = JabberResourceFromJID(mucce->pszUID);
					if (!isSelf(mucce->pszID, nick)) {
						JabberSend(jabberThreadInfo->s, "<p to='%s'><x><i i='%s' a='0'/></x></p>", mucce->pszID, nick);
					}
					mir_free(nick);
				}
				break;
			case MUCC_EVENT_SET_USER_ROLE:
				if (jabberOnline) {
					char *nick;
					nick = JabberResourceFromJID(mucce->pszUID);
					if (!isSelf(mucce->pszID, nick)) {
						if (mucce->dwFlags == MUCC_EF_USER_ADMIN) {
							id = 2;
						} else if (mucce->dwFlags == MUCC_EF_USER_MEMBER) {
							id = 3;
						} else {
							id = 0;
						}
						JabberSend(jabberThreadInfo->s, "<p to='%s'><x><i i='%s' a='%d' /></x></p>", mucce->pszID, nick, id);
					}
					mir_free(nick);
				}
				break;
			case MUCC_EVENT_QUERY_USER_NICKS:
				TlenMUCSendQuery(7, mucce->pszID, 0);
				break;
			case MUCC_EVENT_QUERY_USER_ROOMS:
				TlenMUCSendQuery(8, mucce->pszID, 0);
				break;
			case MUCC_EVENT_QUERY_CONTACTS:
				TlenMUCQueryContacts(mucce->pszID);
				break;
			case MUCC_EVENT_JOIN:
				if (jabberOnline) {
					if (mucce->pszID==NULL || strlen(mucce->pszID)==0) {
						if (mucce->pszName==NULL || strlen(mucce->pszName)==0) { // create a new chat room
							id = JabberSerialNext();
							JabberSend(jabberThreadInfo->s, "<p to='c' tp='c' id='"JABBER_IQID"%d'/>", id);
						} else { // find a chat room by name
							TlenMUCSendQuery(3, mucce->pszName, 0);
						}
					} else { // join existing chat room
						if (!TlenMUCCreateWindow(mucce->pszID, mucce->pszName, mucce->dwFlags, mucce->pszNick, NULL)) {
							TlenMUCSendPresence(mucce->pszID, mucce->pszNick, ID_STATUS_ONLINE);
						}
					}
				}
				break;
			case MUCC_EVENT_START_PRIV:
				if (jabberOnline) {
					JABBER_LIST_ITEM *item;
					item = JabberListGetItemPtr(LIST_CHATROOM, mucce->pszID);
					if (item!=NULL) {
						char *nick;
						nick = JabberResourceFromJID(mucce->pszUID);
						if (!isSelf(mucce->pszID, nick)) {
							if (nick[0]=='~' || item->nick!=NULL) {
								char str[256];
								sprintf(str, "%s/%s", mucce->pszID, nick);
								hContact = JabberDBCreateContact(str, nick, TRUE); //(char *)mucce->pszUID
								DBWriteContactSettingByte(hContact, jabberProtoName, "bChat", TRUE);
								CallService(MS_MSG_SENDMESSAGE, (WPARAM) hContact, (LPARAM) NULL);
							} else {
								DBVARIANT dbv;
								if (!DBGetContactSetting(NULL, jabberProtoName, "LoginServer", &dbv)) {
									char str[512];
									_snprintf(str, sizeof(str), "%s@%s", nick, dbv.pszVal);
									DBFreeVariant(&dbv);
									hContact = JabberDBCreateContact(str, nick, TRUE);
									CallService(MS_MSG_SENDMESSAGE, (WPARAM) hContact, (LPARAM) NULL);
								}
							}
						}
						mir_free(nick);
					}
				}
				break;
		}
	}
	return 0;
}
Ejemplo n.º 6
0
static int ServiceParseXmppURI(WPARAM wParam, LPARAM lParam)
{
	char *arg = (char*)lParam;
	char *jid;
	UNREFERENCED_PARAMETER(wParam);
	if (arg == NULL) return 1; /* sanity check */
  /* skip leading prefix */
	arg = strchr(arg, ':');
	if (arg == NULL) return 1; /* parse failed */
	for (++arg; *arg == '/'; ++arg);
	/*
		complete specification: http://www.xmpp.org/extensions/xep-0147.html
		send message:           xmpp:JID?message;subject=TEXT&body=TEXT
		add user:               xmpp:JID?roster
		remove user:            xmpp:JID?remove
	*/
	/* user id */
	arg = strchr(jid = arg, '?');
	if (arg == NULL) arg += mir_strlen(arg); /* points to terminating nul */
	else *(arg++) = 0;
	if (*jid == 0) return 1; /* parse failed */
	 /* send a message to a contact */
	else if (*arg == 0 || (!_strnicmp(arg, "message", 7) && (*(arg + 7) == ';' || *(arg + 7) == 0))) {
		char *tok, *subj = NULL, *body = NULL;
		MCONTACT hContact;
		char msg[1024];
		arg += 7;
		while (*arg == ';') ++arg;
		tok = strtok(arg, "&"); /* first token */
		while (tok != NULL) {
			if (!_strnicmp(tok, "subject=", 8) && *(tok + 8) != 0)
				subj = Netlib_UrlDecode(tok + 8);
			if (!_strnicmp(tok, "body=", 5) && *(tok + 5) != 0)
				body = Netlib_UrlDecode(tok + 5);
			tok = strtok(NULL, "&"); /* next token */
		}
		if (ServiceExists(MS_MSG_SENDMESSAGE)) {
			hContact = JabberDBCreateContact(jid, jid, TRUE, FALSE);
			if (subj != NULL && body != NULL) {
				mir_snprintf(msg, _countof(msg), "%.128s %s", subj, body);
				body = msg;
			}
			else if (body == NULL) body = subj;
			if (hContact != NULL)
				CallService(MS_MSG_SENDMESSAGE, hContact, (LPARAM)body);
		}
		return 0;
	}
	/* add user to contact list */
	else if (!_strnicmp(arg, "roster", 6) && (*(arg + 6) == ';' || *(arg + 6) == 0)) {
		ADDCONTACTSTRUCT acs;
		PROTOSEARCHRESULT psr;
		if (JabberHContactFromJID(jid) == NULL) { /* does not yet check if jid belongs to current user */
			acs.handleType = HANDLE_SEARCHRESULT;
			acs.szProto = jabberProtoName;
			acs.psr = &psr;
			memset(&psr, 0, sizeof(PROTOSEARCHRESULT));
			psr.cbSize = sizeof(PROTOSEARCHRESULT);
			psr.nick.t = jid;
			CallService(MS_ADDCONTACT_SHOW, 0, (LPARAM)&acs);
		}
		return 0;
	}
	/* remove user from contact list */
	else if (!_strnicmp(arg, "remove", 6) && (*(arg + 6) == ';' || *(arg + 6) == 0)) {
		MCONTACT hContact;
		hContact = JabberHContactFromJID(jid);
		if (hContact == NULL) /* not yet implemented: show standard miranda dialog here */
			CallService(MS_DB_CONTACT_DELETE, hContact, 0);
		return 0;
	}
	/* add user subscription */
	else if (!_strnicmp(arg, "subscribe", 9) && (*(arg + 9) == ';' || *(arg + 9) == 0)) {
		/* not yet implemented */
		return 0;
	}
	/* remove user subscription */
	else if (!_strnicmp(arg, "unsubscribe", 11) && (*(arg + 11) == ';' || *(arg + 11) == 0)) {
		/* not yet implemented */
		return 0;
	}
	return 1; /* parse failed */
}