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; |