comparison mcabber/src/jab_iq.c @ 1158:c30c315dc447

XEP-0146 support (Remote Controlling Clients)
author misc@mandriva.org
date Thu, 15 Feb 2007 21:49:39 +0100
parents 995dde656033
children 53c0c5be43fa
comparison
equal deleted inserted replaced
1157:5c857f0f0ab8 1158:c30c315dc447
30 #include "roster.h" 30 #include "roster.h"
31 #include "utils.h" 31 #include "utils.h"
32 #include "screen.h" 32 #include "screen.h"
33 #include "settings.h" 33 #include "settings.h"
34 #include "hbuf.h" 34 #include "hbuf.h"
35 #include "commands.h"
35 36
36 37
37 // Bookmarks for IQ:private storage 38 // Bookmarks for IQ:private storage
38 xmlnode bookmarks; 39 xmlnode bookmarks;
39 // Roster notes for IQ:private storage 40 // Roster notes for IQ:private storage
49 vcard_voice = 1<<3, 50 vcard_voice = 1<<3,
50 vcard_fax = 1<<4, 51 vcard_fax = 1<<4,
51 vcard_cell = 1<<5, 52 vcard_cell = 1<<5,
52 vcard_inet = 1<<6, 53 vcard_inet = 1<<6,
53 vcard_pref = 1<<7, 54 vcard_pref = 1<<7,
55 };
56
57 static void handle_iq_command_set_status(jconn conn, char *from,
58 const char *id, xmlnode xmldata);
59
60 static void handle_iq_command_leave_groupchats(jconn conn, char *from,
61 const char *id, xmlnode xmldata);
62
63 typedef void (*adhoc_command_callback)(jconn, char*, const char*, xmlnode);
64
65 struct adhoc_command {
66 char *name;
67 char *description;
68 bool only_for_self;
69 adhoc_command_callback callback;
70 };
71
72 const struct adhoc_command adhoc_command_list[] = {
73 { "http://jabber.org/protocol/rc#set-status",
74 "Set the client as away",
75 1,
76 &handle_iq_command_set_status },
77 { "http://jabber.org/protocol/rc#leave-groupchats",
78 "Leave groupchats",
79 1,
80 &handle_iq_command_leave_groupchats },
81 { NULL, NULL, 0, NULL },
82 };
83
84 struct adhoc_status {
85 char *name; // the name used by adhoc
86 char *description;
87 char *status; // the string, used by setstus
88 };
89
90 const struct adhoc_status adhoc_status_list[] = {
91 {"online", "Online", "avail"},
92 {"chat", "Chat", "free"},
93 {"away", "Away", "away"},
94 {"xd", "Extended away", "notavail"},
95 {"dnd", "Do not disturb", "dnd"},
96 {"invisible", "Invisible", "invisible"},
97 {"offline", "Offline", "offline"},
98 {NULL, NULL, NULL},
54 }; 99 };
55 100
56 // iqs_new(type, namespace, prefix, timeout) 101 // iqs_new(type, namespace, prefix, timeout)
57 // Create a query (GET, SET) IQ structure. This function should not be used 102 // Create a query (GET, SET) IQ structure. This function should not be used
58 // for RESULT packets. 103 // for RESULT packets.
792 // so we should be there only once. (That's ugly, however) 837 // so we should be there only once. (That's ugly, however)
793 jb_setprevstatus(); 838 jb_setprevstatus();
794 } 839 }
795 } 840 }
796 841
842 // FIXME highly duplicated code
843 // factorisation is doable
844 static void send_iq_not_implemented(jconn conn, char *from, xmlnode xmldata)
845 {
846 xmlnode x, y, z;
847 // Not implemented.
848 x = xmlnode_dup(xmldata);
849 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
850 xmlnode_hide_attrib(x, "from");
851
852 xmlnode_put_attrib(x, "type", TMSG_ERROR);
853 y = xmlnode_insert_tag(x, TMSG_ERROR);
854 xmlnode_put_attrib(y, "code", "501");
855 xmlnode_put_attrib(y, "type", "cancel");
856 z = xmlnode_insert_tag(y, "feature-not-implemented");
857 xmlnode_put_attrib(z, "xmlns", NS_XMPP_STANZAS);
858
859 jab_send(conn, x);
860 xmlnode_free(x);
861 }
862
863 /*
864 static void send_iq_commands_bad_action(jconn conn, char *from, xmlnode xmldata)
865 {
866 xmlnode x, y, z;
867
868 x = xmlnode_dup(xmldata);
869 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
870 xmlnode_hide_attrib(x, "from");
871
872 xmlnode_put_attrib(x, "type", TMSG_ERROR);
873 y = xmlnode_insert_tag(x, TMSG_ERROR);
874 xmlnode_put_attrib(y, "code", "400");
875 xmlnode_put_attrib(y, "type", "modify");
876 z = xmlnode_insert_tag(y, "bad-request");
877 xmlnode_put_attrib(z, "xmlns", NS_XMPP_STANZAS);
878 z = xmlnode_insert_tag(y, "bad-action");
879 xmlnode_put_attrib(z, "xmlns", NS_COMMANDS);
880
881 jab_send(conn, x);
882 xmlnode_free(x);
883 }
884 */
885
886 static void send_iq_forbidden(jconn conn, char *from, xmlnode xmldata)
887 {
888 xmlnode x, y, z;
889
890 x = xmlnode_dup(xmldata);
891 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
892 xmlnode_hide_attrib(x, "from");
893
894 xmlnode_put_attrib(x, "type", TMSG_ERROR);
895 y = xmlnode_insert_tag(x, TMSG_ERROR);
896 xmlnode_put_attrib(y, "code", "403");
897 xmlnode_put_attrib(y, "type", "cancel");
898 z = xmlnode_insert_tag(y, "forbidden");
899 xmlnode_put_attrib(z, "xmlns", NS_XMPP_STANZAS);
900
901 jab_send(conn, x);
902 xmlnode_free(x);
903 }
904
905 static void send_iq_commands_malformed_action(jconn conn, char *from,
906 xmlnode xmldata)
907 {
908 xmlnode x, y, z;
909
910 x = xmlnode_dup(xmldata);
911 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
912 xmlnode_hide_attrib(x, "from");
913
914 xmlnode_put_attrib(x, "type", TMSG_ERROR);
915 y = xmlnode_insert_tag(x, TMSG_ERROR);
916 xmlnode_put_attrib(y, "code", "400");
917 xmlnode_put_attrib(y, "type", "modify");
918 z = xmlnode_insert_tag(y, "bad-request");
919 xmlnode_put_attrib(z, "xmlns", NS_XMPP_STANZAS);
920 z = xmlnode_insert_tag(y, "malformed-action");
921 xmlnode_put_attrib(z, "xmlns", NS_COMMANDS);
922
923 jab_send(conn, x);
924 xmlnode_free(x);
925 }
926
927 static void handle_iq_commands_list(jconn conn, char *from, const char *id,
928 xmlnode xmldata)
929 {
930 xmlnode x;
931 xmlnode myquery;
932 jid requester_jid;
933 const struct adhoc_command *command;
934 bool from_self;
935 x = jutil_iqnew(JPACKET__RESULT, NS_DISCO_ITEMS);
936 xmlnode_put_attrib(x, "id", id);
937 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
938 myquery = xmlnode_get_tag(x, "query");
939
940 requester_jid = jid_new(conn->p, xmlnode_get_attrib(xmldata, "from"));
941 from_self = !jid_cmpx(conn->user, requester_jid, JID_USER | JID_SERVER);
942
943 for (command = adhoc_command_list ; command->name ; command++) {
944 if (!command->only_for_self || from_self) {
945 xmlnode item;
946 item = xmlnode_insert_tag(myquery, "item");
947 xmlnode_put_attrib(item, "node", command->name);
948 xmlnode_put_attrib(item, "name", command->description);
949 xmlnode_put_attrib(item, "jid", jid_full(conn->user));
950 }
951 }
952
953 jab_send(jc, x);
954 xmlnode_free(x);
955 }
956
957 static void xmlnode_insert_dataform_result_message(xmlnode node, char *message)
958 {
959 xmlnode x = xmlnode_insert_tag(node, "x");
960 xmlnode_put_attrib(x, "type", "result");
961 xmlnode_put_attrib(x, "xmlns", "jabber:x:data");
962
963 xmlnode field = xmlnode_insert_tag(x, "field");
964 xmlnode_put_attrib(field, "type", "text-single");
965 xmlnode_put_attrib(field, "var", "message");
966
967 xmlnode value = xmlnode_insert_tag(field, "value");
968 xmlnode_insert_cdata(value, message, -1);
969 }
970
971 static char *generate_session_id(char *prefix)
972 {
973 char *result;
974 static int counter = 0;
975 counter++;
976 // TODO better use timezone ?
977 result = g_strdup_printf("%s-%i", prefix, counter);
978 return result;
979 }
980
981 static void handle_iq_command_set_status(jconn conn, char *from, const char *id,
982 xmlnode xmldata)
983 {
984 char *action, *node, *sessionid;
985 xmlnode iq, command, x, y;
986 const struct adhoc_status *s;
987
988 x = xmlnode_get_tag(xmldata, "command");
989 action = xmlnode_get_attrib(x, "action");
990 node = xmlnode_get_attrib(x, "node");
991 sessionid = xmlnode_get_attrib(x, "sessionid");
992
993 iq = xmlnode_new_tag("iq");
994 command = xmlnode_insert_tag(iq, "command");
995 xmlnode_put_attrib(command, "node", node);
996 xmlnode_put_attrib(command, "xmlns", NS_COMMANDS);
997
998 if (!sessionid) {
999 sessionid = generate_session_id("set-status");
1000 xmlnode_put_attrib(command, "sessionid", sessionid);
1001 g_free(sessionid);
1002 xmlnode_put_attrib(command, "status", "executing");
1003
1004 x = xmlnode_insert_tag(command, "x");
1005 xmlnode_put_attrib(x, "type", "form");
1006 xmlnode_put_attrib(x, "xmlns", "jabber:x:data");
1007
1008 y = xmlnode_insert_tag(x, "title");
1009 xmlnode_insert_cdata(y, "Change Status", -1);
1010
1011 y = xmlnode_insert_tag(x, "instructions");
1012 xmlnode_insert_cdata(y, "Choose the status and status message", -1);
1013
1014 // TODO see if factorisation is possible
1015 // (with xmlnode_insert_dataform_result_message)
1016 y = xmlnode_insert_tag(x, "field");
1017 xmlnode_put_attrib(y, "type", "hidden");
1018 xmlnode_put_attrib(y, "var", "FORM_TYPE");
1019
1020 xmlnode value = xmlnode_insert_tag(y, "value");
1021 xmlnode_insert_cdata(value, "http://jabber.org/protocol/rc", -1);
1022
1023 y = xmlnode_insert_tag(x, "field");
1024 xmlnode_put_attrib(y, "type", "list-single");
1025 xmlnode_put_attrib(y, "var", "status");
1026 xmlnode_put_attrib(y, "label", "Status");
1027 xmlnode_insert_tag(y, "required");
1028
1029 value = xmlnode_insert_tag(y, "value");
1030 // TODO current status
1031 xmlnode_insert_cdata(value, "online", -1);
1032 for (s = adhoc_status_list; s->name; s++) {
1033 xmlnode option = xmlnode_insert_tag(y, "option");
1034 value = xmlnode_insert_tag(option, "value");
1035 xmlnode_insert_cdata(value, s->name, -1);
1036 xmlnode_put_attrib(option, "label", s->description);
1037 }
1038 // TODO add priority ?
1039 // I do not think this is useful, user should not have to care of the
1040 // priority like gossip and gajim do (misc)
1041 y = xmlnode_insert_tag(x, "field");
1042 xmlnode_put_attrib(y, "type", "text-multi");
1043 xmlnode_put_attrib(y, "var", "status-message");
1044 xmlnode_put_attrib(y, "label", "Message");
1045 }
1046 else // (if sessionid)
1047 {
1048 y = xmlnode_get_tag(x, "x?xmlns=jabber:x:data");
1049 if (y) {
1050 char *value, *message;
1051 value = xmlnode_get_tag_data(xmlnode_get_tag(y, "field?var=status"),
1052 "value");
1053 message = xmlnode_get_tag_data(xmlnode_get_tag(y,
1054 "field?var=status-message"), "value");
1055 for (s = adhoc_status_list; !s->name || strcmp(s->name, value); s++);
1056 if (s->name) {
1057 char* status = g_strdup_printf("%s %s", s->status, message);
1058 xmlnode_put_attrib(command, "status", "completed");
1059
1060 setstatus(NULL, status);
1061 g_free(status);
1062 xmlnode_put_attrib(iq, "type", "result");
1063 xmlnode_insert_dataform_result_message(command, "Status was changed");
1064 }
1065 }
1066 }
1067 xmlnode_put_attrib(iq, "to", xmlnode_get_attrib(xmldata, "from"));
1068 xmlnode_put_attrib(iq, "id", id);
1069 jab_send(jc, iq);
1070 xmlnode_free(iq);
1071 }
1072
1073 static void _callback_foreach_buddy_groupchat(gpointer rosterdata, void *param)
1074 {
1075 xmlnode value;
1076 xmlnode *field;
1077 const char *room_jid, *nickname;
1078 char *desc;
1079
1080 room_jid = buddy_getjid(rosterdata);
1081 if (!room_jid) return;
1082 nickname = buddy_getnickname(rosterdata);
1083 if (!nickname) return;
1084 field = param;
1085
1086 xmlnode option = xmlnode_insert_tag(*field, "option");
1087 value = xmlnode_insert_tag(option, "value");
1088 xmlnode_insert_cdata(value, room_jid, -1);
1089 desc = g_strdup_printf("%s on %s", nickname, room_jid);
1090 xmlnode_put_attrib(option, "label", desc);
1091 g_free(desc);
1092 }
1093
1094 static void handle_iq_command_leave_groupchats(jconn conn, char *from, const char *id,
1095 xmlnode xmldata)
1096 {
1097 char *action, *node, *sessionid;
1098 xmlnode iq, command, x;
1099
1100 x = xmlnode_get_tag(xmldata, "command");
1101 action = xmlnode_get_attrib(x, "action");
1102 node = xmlnode_get_attrib(x, "node");
1103 sessionid = xmlnode_get_attrib(x, "sessionid");
1104
1105 iq = xmlnode_new_tag("iq");
1106 command = xmlnode_insert_tag(iq, "command");
1107 xmlnode_put_attrib(command, "node", node);
1108 xmlnode_put_attrib(command, "xmlns", NS_COMMANDS);
1109
1110 if (!sessionid) {
1111 sessionid = generate_session_id("leave-groupchats");
1112 xmlnode_put_attrib(command, "sessionid", sessionid);
1113 g_free(sessionid);
1114 xmlnode_put_attrib(command, "status", "executing");
1115
1116 x = xmlnode_insert_tag(command, "x");
1117 xmlnode_put_attrib(x, "type", "form");
1118 xmlnode_put_attrib(x, "xmlns", "jabber:x:data");
1119
1120 xmlnode title = xmlnode_insert_tag(x, "title");
1121 xmlnode_insert_cdata(title, "Leave groupchats", -1);
1122
1123 xmlnode instructions = xmlnode_insert_tag(x, "instructions");
1124 xmlnode_insert_cdata(instructions, "What groupchats do you want to leave ?",
1125 -1);
1126
1127 xmlnode field = xmlnode_insert_tag(x, "field");
1128 xmlnode_put_attrib(field, "type", "hidden");
1129 xmlnode_put_attrib(field, "var", "FORM_TYPE");
1130
1131 xmlnode value = xmlnode_insert_tag(field, "value");
1132 xmlnode_insert_cdata(value, "http://jabber.org/protocol/rc", -1);
1133
1134 field = xmlnode_insert_tag(x, "field");
1135 xmlnode_put_attrib(field, "type", "list-multi");
1136 xmlnode_put_attrib(field, "var", "groupchats");
1137 xmlnode_put_attrib(field, "label", "Groupchats : ");
1138 xmlnode_insert_tag(field, "required");
1139
1140 foreach_buddy(ROSTER_TYPE_ROOM, &_callback_foreach_buddy_groupchat, &field);
1141 }
1142 else // (if sessionid)
1143 {
1144 xmlnode form = xmlnode_get_tag(x, "x?xmlns=jabber:x:data");
1145 if (form) {
1146 xmlnode_put_attrib(command, "status", "completed");
1147 xmlnode gc = xmlnode_get_tag(form, "field?var=groupchats");
1148 xmlnode x;
1149
1150 for (x = xmlnode_get_firstchild(gc) ; x ; x = xmlnode_get_nextsibling(x)) {
1151 char* to_leave = xmlnode_get_tag_data(x, "value");
1152 if (to_leave) {
1153 GList* b = buddy_search_jid(to_leave);
1154 if (b)
1155 room_leave(b->data, "Asked by remote command");
1156 }
1157 }
1158 xmlnode_put_attrib(iq, "type", "result");
1159 xmlnode_insert_dataform_result_message(command, "Groupchats were leaved");
1160 }
1161 }
1162 xmlnode_put_attrib(iq, "to", xmlnode_get_attrib(xmldata, "from"));
1163 xmlnode_put_attrib(iq, "id", id);
1164 jab_send(jc, iq);
1165 xmlnode_free(iq);
1166 }
1167
1168 static void handle_iq_commands(jconn conn, char *from, const char *id,
1169 xmlnode xmldata)
1170 {
1171 jid requester_jid;
1172 xmlnode x;
1173 const struct adhoc_command *command;
1174
1175 requester_jid = jid_new(conn->p, xmlnode_get_attrib(xmldata, "from"));
1176 x = xmlnode_get_tag(xmldata, "command");
1177 if (!jid_cmpx(conn->user, requester_jid, JID_USER | JID_SERVER) ) {
1178 char *action, *node;
1179 action = xmlnode_get_attrib(x, "action");
1180 node = xmlnode_get_attrib(x, "node");
1181 // action can be NULL, in which case it seems to take the default,
1182 // ie execute
1183 if (!action || !strcmp(action, "execute") || !strcmp(action, "cancel")
1184 || !strcmp(action, "next") || !strcmp(action, "complete")) {
1185 for (command = adhoc_command_list; command->name; command++) {
1186 if (!strcmp(node, command->name))
1187 command->callback(conn, from, id, xmldata);
1188 }
1189 // "prev" action will get there, as we do not implement it, and do not autorise it
1190 } else {
1191 send_iq_commands_malformed_action(conn, from, xmldata);
1192 }
1193 } else {
1194 send_iq_forbidden(conn, from, xmldata);
1195 }
1196 }
1197
1198 static void handle_iq_disco_items(jconn conn, char *from, const char *id,
1199 xmlnode xmldata)
1200 {
1201 xmlnode x;
1202 char *node;
1203 x = xmlnode_get_tag(xmldata, "query");
1204 node = xmlnode_get_attrib(x, "node");
1205 if (node) {
1206 if (!strcmp(node, NS_COMMANDS)) {
1207 handle_iq_commands_list(conn, from, id, xmldata);
1208 } else {
1209 send_iq_not_implemented(conn, from, xmldata);
1210 }
1211 } else {
1212 // not sure about this one
1213 send_iq_not_implemented(conn, from, xmldata);
1214 }
1215 }
1216
797 static void handle_iq_disco_info(jconn conn, char *from, const char *id, 1217 static void handle_iq_disco_info(jconn conn, char *from, const char *id,
798 xmlnode xmldata) 1218 xmlnode xmldata)
799 { 1219 {
800 xmlnode x, y; 1220 xmlnode x, y;
801 xmlnode myquery; 1221 xmlnode myquery;
820 "var", NS_TIME); 1240 "var", NS_TIME);
821 xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"), 1241 xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
822 "var", NS_VERSION); 1242 "var", NS_VERSION);
823 xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"), 1243 xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
824 "var", NS_PING); 1244 "var", NS_PING);
1245 xmlnode_put_attrib(xmlnode_insert_tag(myquery, "feature"),
1246 "var", NS_COMMANDS);
825 jab_send(jc, x); 1247 jab_send(jc, x);
826 xmlnode_free(x); 1248 xmlnode_free(x);
827 } 1249 }
828 1250
829 static void handle_iq_ping(jconn conn, char *from, const char *id, 1251 static void handle_iq_ping(jconn conn, char *from, const char *id,
917 1339
918 // This function borrows some code from the Gaim project 1340 // This function borrows some code from the Gaim project
919 static void handle_iq_get(jconn conn, char *from, xmlnode xmldata) 1341 static void handle_iq_get(jconn conn, char *from, xmlnode xmldata)
920 { 1342 {
921 const char *id, *ns; 1343 const char *id, *ns;
922 xmlnode x, y, z; 1344 xmlnode x;
923 guint iq_not_implemented = FALSE; 1345 guint iq_not_implemented = FALSE;
924 1346
925 id = xmlnode_get_attrib(xmldata, "id"); 1347 id = xmlnode_get_attrib(xmldata, "id");
926 if (!id) { 1348 if (!id) {
927 scr_LogPrint(LPRINT_LOG, "IQ get stanza with no ID, ignored."); 1349 scr_LogPrint(LPRINT_LOG, "IQ get stanza with no ID, ignored.");
937 1359
938 x = xmlnode_get_tag(xmldata, "query"); 1360 x = xmlnode_get_tag(xmldata, "query");
939 ns = xmlnode_get_attrib(x, "xmlns"); 1361 ns = xmlnode_get_attrib(x, "xmlns");
940 if (ns && !strcmp(ns, NS_DISCO_INFO)) { 1362 if (ns && !strcmp(ns, NS_DISCO_INFO)) {
941 handle_iq_disco_info(conn, from, id, xmldata); 1363 handle_iq_disco_info(conn, from, id, xmldata);
1364 } else if (ns && !strcmp(ns, NS_DISCO_ITEMS)) {
1365 handle_iq_disco_items(conn, from, id, xmldata);
942 } else if (ns && !strcmp(ns, NS_VERSION)) { 1366 } else if (ns && !strcmp(ns, NS_VERSION)) {
943 handle_iq_version(conn, from, id, xmldata); 1367 handle_iq_version(conn, from, id, xmldata);
944 } else if (ns && !strcmp(ns, NS_TIME)) { 1368 } else if (ns && !strcmp(ns, NS_TIME)) {
945 handle_iq_time(conn, from, id, xmldata); 1369 handle_iq_time(conn, from, id, xmldata);
946 } else { 1370 } else {
948 } 1372 }
949 1373
950 if (!iq_not_implemented) 1374 if (!iq_not_implemented)
951 return; 1375 return;
952 1376
953 // Not implemented. 1377 send_iq_not_implemented(conn, from, xmldata);
954 x = xmlnode_dup(xmldata);
955 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
956 xmlnode_hide_attrib(x, "from");
957
958 xmlnode_put_attrib(x, "type", TMSG_ERROR);
959 y = xmlnode_insert_tag(x, TMSG_ERROR);
960 xmlnode_put_attrib(y, "code", "501");
961 xmlnode_put_attrib(y, "type", "cancel");
962 z = xmlnode_insert_tag(y, "feature-not-implemented");
963 xmlnode_put_attrib(z, "xmlns", NS_XMPP_STANZAS);
964
965 jab_send(conn, x);
966 xmlnode_free(x);
967 } 1378 }
968 1379
969 static void handle_iq_set(jconn conn, char *from, xmlnode xmldata) 1380 static void handle_iq_set(jconn conn, char *from, xmlnode xmldata)
970 { 1381 {
971 const char *id, *ns; 1382 const char *id, *ns;
972 xmlnode x, y, z; 1383 xmlnode x;
973 guint iq_not_implemented = FALSE; 1384 guint iq_not_implemented = FALSE;
974 1385
975 id = xmlnode_get_attrib(xmldata, "id"); 1386 id = xmlnode_get_attrib(xmldata, "id");
976 if (!id) 1387 if (!id)
977 scr_LogPrint(LPRINT_LOG, "IQ set stanza with no ID..."); 1388 scr_LogPrint(LPRINT_LOG, "IQ set stanza with no ID...");
979 x = xmlnode_get_tag(xmldata, "query"); 1390 x = xmlnode_get_tag(xmldata, "query");
980 ns = xmlnode_get_attrib(x, "xmlns"); 1391 ns = xmlnode_get_attrib(x, "xmlns");
981 if (ns && !strcmp(ns, NS_ROSTER)) { 1392 if (ns && !strcmp(ns, NS_ROSTER)) {
982 handle_iq_roster(x); 1393 handle_iq_roster(x);
983 } else { 1394 } else {
984 iq_not_implemented = TRUE; 1395 x = xmlnode_get_tag(xmldata, "command");
1396 ns = xmlnode_get_attrib(x, "xmlns");
1397 if (ns && !strcmp(ns, NS_COMMANDS)) {
1398 handle_iq_commands(conn, from, id, xmldata);
1399 } else {
1400 iq_not_implemented = TRUE;
1401 }
985 } 1402 }
986 1403
987 if (!id) return; 1404 if (!id) return;
988 1405
989 if (!iq_not_implemented) { 1406 if (!iq_not_implemented) {
990 x = xmlnode_new_tag("iq"); 1407 x = xmlnode_new_tag("iq");
991 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from")); 1408 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
992 xmlnode_put_attrib(x, "type", "result"); 1409 xmlnode_put_attrib(x, "type", "result");
993 xmlnode_put_attrib(x, "id", id); 1410 xmlnode_put_attrib(x, "id", id);
1411 jab_send(conn, x);
1412 xmlnode_free(x);
994 } else { 1413 } else {
995 /* Not implemented yet: send an error stanza */ 1414 send_iq_not_implemented(conn, from, xmldata);
996 x = xmlnode_dup(xmldata); 1415 }
997 xmlnode_put_attrib(x, "to", xmlnode_get_attrib(xmldata, "from"));
998 xmlnode_hide_attrib(x, "from");
999 xmlnode_put_attrib(x, "type", "result");
1000 xmlnode_put_attrib(x, "type", TMSG_ERROR);
1001 y = xmlnode_insert_tag(x, TMSG_ERROR);
1002 xmlnode_put_attrib(y, "code", "501");
1003 xmlnode_put_attrib(y, "type", "cancel");
1004 z = xmlnode_insert_tag(y, "feature-not-implemented");
1005 xmlnode_put_attrib(z, "xmlns", NS_XMPP_STANZAS);
1006 }
1007
1008 jab_send(conn, x);
1009 xmlnode_free(x);
1010 } 1416 }
1011 1417
1012 void handle_packet_iq(jconn conn, char *type, char *from, xmlnode xmldata) 1418 void handle_packet_iq(jconn conn, char *type, char *from, xmlnode xmldata)
1013 { 1419 {
1014 if (!type) 1420 if (!type)