1/*!
2 * @file main.c
3 *
4 * vim: expandtab:ts=2:sts=2:sw=2
5 *
6 * @authors
7 * Copyright (C) 2020 Anoxinon e.V.
8 *
9 * @copyright
10 * This file is part of xmppc.
11 *
12 * xmppc is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * xmppc is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with Foobar. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * German
26 *
27 * Diese Datei ist Teil von xmppc.
28 *
29 * xmppc ist Freie Software: Sie können es unter den Bedingungen
30 * der GNU General Public License, wie von der Free Software Foundation,
31 * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren
32 * veröffentlichten Version, weiter verteilen und/oder modifizieren.
33 *
34 * xmppc wird in der Hoffnung, dass es nützlich sein wird, aber
35 * OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
36 * Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
37 * Siehe die GNU General Public License für weitere Details.
38 *
39 * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
40 * Programm erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.
41 */
42
43/*!
44 * @mainpage xmppc - XMPP command line
45 *
46 * @section whatIsXmppc What is xmppc
47 *
48 * xmppc is a command line program for XMPP (https://xmpp.org/).
49 * The program has been written for the Debian GNU/Linux system (https://www.debian.org/).
50 *
51 * @section build Build
52 *
53 * @subsection dependencies Dependencies
54 *
55 * - The GNU C Library (glibc) - 2.28-10
56 * - GLib - 2.58.3
57 * - XMPP library libstrophe - 0.9.2-2
58 * - GPGME (GnuPG Made Easy) - 1.12.0-6
59 *
60 * @subsection compile Compile
61 *
62 * @code{.bash}
63 * ./bootstrap.sh
64 * ./configure
65 * make
66 * @endcode
67 *
68 * @subsection documentation Documentation
69 * @code{.bash}
70 * cd doc
71 * make
72 * @endcode
73 * @section usage Usage
74 *
75 * @code{.bash}
76 * xmppc --jid <jabberid> --pwd <secret> --mode <mode> <command> <command args>
77 * @endcode
78 *
79 * @code{.bash}
80 * xmppc --jid user@domain.tld --pwd "my secret" --mode pgp chat friend@domain.tld "Hello!"
81 * xmppc --jid user@domain.tld --pwd $(pass XMPP/domain/user) --mode omemo list
82 * @endcode
83 *
84 *
85 * @section development Development
86 *
87 * - \subpage module
88 *
89 */
90
91/*!
92 * \page module Modules
93 *
94 * \section account Account
95 * \section roster Roster
96 * \section message Message
97 * \section muc Multi-User-Chat
98 * \section omemo OMEMO
99 * XEP-0384: OMEMO Encryption (Version 0.3.0 (2018-07-31)). xmppc can use used
100 * to request the users OMEMO Device ID List and displays the Fingerprints of
101 * the keys.
102 *
103 * @code{.bash}
104 * xmppc --jid user@domain.tld --pwd $(pass domain.tld/user) --mode omemo
105 * @endcode
106 *
107 * @code{.bash}
108 * xmppc --jid user@domain.tld --pwd $(pass domain.tld/user) --mode omemo| gpg --clear-sign --sign-with 123ABC_MY_OPENPGP_KEY_1234 > omemo.asc
109 * @endcode
110 *
111 *
112 * \section pgp PGP
113 * XEP-0027: Current Jabber OpenPGP Usage
114 * (https://xmpp.org/extensions/xep-0027.html) is obsolete, but there are still
115 * clients which supports XEP-0027 instead of XEP-0373 / XEP-0374. This module
116 * supports sending of OpenPGP encrypted messages via XMPP's XEP-0027.
117 *
118 * @code{.bash}
119 * xmppc --jid user@domain.tld --pwd "my secret" --mode pgp friend@domain.tld "Hello!"
120 * @endcode
121 *
122 * xmppc use GnuPG's GPGME API use lookup the own key and the key of the
123 * recipient. Those two keys will be used to encrypt the message.
124 *
125 * Details \subpage module-pgp
126 *
127 */
128
129#include "config.h"
130#include <assert.h>
131#include <getopt.h>
132#include <stdarg.h>
133#include <stdio.h>
134#include <stdlib.h>
135#include <string.h>
136#include <glib.h>
137#include <strophe.h>
138#include <errno.h>
139#include <termios.h>
140#include <unistd.h>
141
142#include "xmppc.h"
143#include "mode/account.h"
144#include "mode/message.h"
145#include "mode/muc.h"
146#include "mode/omemo.h"
147#include "mode/roster.h"
148#include "mode/pgp.h"
149#include "mode/openpgp.h"
150#include "mode/monitor.h"
151#include "mode/mam.h"
152#include "mode/discovery.h"
153#include "mode/bookmark.h"
154
155/*!
156 * @brief The callback structure.
157 *
158 * This struct is used to call the "execution"-function of the mode module (e.g
159 * account_execute_command, roster_execute_command, ...).
160 *
161 * @authors DebxWoody
162 * @since 0.0.2
163 * @version 1
164 *
165 */
166
167typedef struct {
168 /*! Size of arguments */
169 int argc;
170 /*! Array of C-Strings (arguments) with size of argc */
171 char **argv;
172 /*! Pointer to Execute-Handler */
173 ExecuteHandler callback;
174 /*! XMPPC context object */
175 xmppc_t *xmppc;
176} callback_t;
177
178/*!
179 * @brief Mode Mapping.
180 *
181 * This struct is used to provider the mapping of the mode name (name via
182 * command line interface of the user (e.g. -m account). The technical ID of the
183 * mode which is defined via enum xmppc_mode_t and the pointer of function which
184 * should be used to handle those mode.
185 *
186 * @authors DebxWoody
187 * @since 0.0.2
188 * @version 1
189 */
190
191struct mode_mapping {
192 const char *name;
193 xmppc_mode_t mode;
194 ExecuteHandler callback;
195};
196
197/*!
198 * @brief Mode mapping table.
199 *
200 * Mapping of mode's string, enum and function.
201 *
202 * @authors DebxWoody
203 * @since 0.0.2
204 * @version 1
205 *
206 */
207
208static struct mode_mapping map[] = {
209 /*!
210 * Account Mode Mapping
211 * @since 0.0.2
212 * @version 1
213 */
214 {"account", ACCOUNT, account_execute_command},
215 /*!
216 * roster Mode Mapping
217 * @since 0.0.2
218 * @version 1
219 */
220 {"roster", ROSTER, roster_execute_command},
221 /*!
222 * Message Mode Mapping
223 * @since 0.0.2
224 * @version 1
225 */
226 {"message", MESSAGE, message_execute_command},
227 /*!
228 * MUC Mode Mapping
229 * @since 0.0.2
230 * @version 1
231 */
232 {"muc", MUC, muc_execute_command},
233 /*!
234 * OMEMO Mode Mapping
235 * @since 0.0.2
236 * @version 1
237 */
238 {"omemo", OMEMO, omemo_execute_command},
239 /*!
240 * PGP Mode Mapping
241 * @since 0.0.2
242 * @version 1
243 */
244 {"pgp", PGP, pgp_execute_command},
245 /*!
246 * XEP-0373: OpenPGP for XMPP
247 */
248 {"openpgp", OPENPGP, openpgp_execute_command},
249 /*!
250 * Monitor
251 */
252 {"monitor", MONITOR, monitor_execute_command},
253 //
254 {"discovery", DISCOVERY, discovery_execute_command},
255 //
256 {"bookmark", BOOKMARK, bookmark_execute_command},
257 //
258 {"mam", MAM, mam_execute_command},
259 // End of Map
260 {NULL, 0}
261};
262
263/*!
264 * \brief Connection Handler Callback of libstrophe.
265 *
266 * This function will be called by libstrophe.
267 *
268 * \param conn See libstrophe documentation
269 * \param status See libstrophe documentation
270 * \param error See libstrophe documentation
271 * \param stream_error See libstrophe documentation
272 *
273 * \param userdata callback_t object for the mode request by user.
274 *
275 * @authors DebxWoody
276 * @since 0.0.2
277 * @version 1
278 *
279 */
280
281void conn_handler(xmpp_conn_t *const conn, const xmpp_conn_event_t status,
282 const int error, xmpp_stream_error_t *const stream_error,
283 void *const userdata) {
284 callback_t *callback = (callback_t *)userdata;
285
286 if( error != 0 ) {
287 logError(callback->xmppc,"Connection failed. %s\n", strerror(error));
288 xmpp_stop(xmpp_conn_get_context(conn));
289 return;
290 }
291
292 if( stream_error != NULL ) {
293 logError(callback->xmppc,"Connection failed. %s\n", stream_error->text);
294 xmpp_stop(xmpp_conn_get_context(conn));
295 return;
296 }
297
298
299 switch (status) {
300 case XMPP_CONN_CONNECT:
301 logInfo(callback->xmppc, "Connected\n");
302 if( xmpp_conn_is_secured(conn) ) {
303 logInfo(callback->xmppc, "Secure connection!\n");
304 } else {
305 logWarn(callback->xmppc, "Connection not secure!\n");
306 }
307 callback->callback(callback->xmppc, callback->argc, callback->argv);
308 break;
309 case XMPP_CONN_RAW_CONNECT:
310 case XMPP_CONN_DISCONNECT:
311 case XMPP_CONN_FAIL:
312 logInfo(callback->xmppc, "Stopping XMPP!\n");
313 xmpp_stop(xmpp_conn_get_context(conn));
314 }
315}
316
317static void _show_help();
318
319/*!
320 * \brief C-Main function
321 *
322 * - Parse argv arguments of the command line.
323 * - Checks jid and password for xmpp logging
324 * - Checks mode requested by user
325 * - All other arguments will be used as mode parameters
326 *
327 * \param argc Arguments counter
328 * \param argv Arguments vector
329 * \returns Exit Code
330 *
331 * @authors DebxWoody
332 * @since 0.0.2
333 * @version 2
334 */
335
336int main(int argc, char *argv[]) {
337#if XMPPC_DEVELOPMENT
338 printf("!!! WARNING: XMPPC is running in development mode !!!\n");
339#endif
340
341 INIT_XMPPC(xmppc);
342
343 static int verbose_flag = 0;
344 int c = 0;
345 xmppc_mode_t mode = UNKOWN;
346 char *jid = NULL;
347 char *pwd = NULL;
348 char *account = NULL;
349
350 static struct option long_options[] = {
351 /* These options set a flag. */
352 {"verbose", no_argument, &verbose_flag, 1},
353 {"help", no_argument, 0, 'h'},
354 {"config", required_argument, 0, 'c'},
355 {"account", required_argument, 0, 'a'},
356 {"jid", required_argument, 0, 'j'},
357 {"pwd", required_argument, 0, 'p'},
358 {"mode", required_argument, 0, 'm'},
359 {"file", required_argument, 0, 'f'},
360 {0, 0, 0, 0}};
361
362 // No arguments provided
363 if (argc == 1) {
364 _show_help();
365 return EXIT_SUCCESS;
366 }
367
368 while (c > -1) {
369 int option_index = 0;
370
371 c = getopt_long(argc, argv, "hva:j:p:m:", long_options, &option_index);
372 if (c > -1) {
373 switch (c) {
374 case 'h':
375 _show_help();
376 return EXIT_SUCCESS;
377
378 case 'c':
379 printf("option -c with value `%s'\n", optarg);
380 break;
381
382 case 'f':
383 printf("option -f with value `%s'\n", optarg);
384 break;
385
386 case 'a':
387 account = malloc(strlen(optarg) + 1);
388 strcpy(account, optarg);
389 break;
390
391 case 'j':
392 jid = malloc(strlen(optarg) + 1);
393 strcpy(jid, optarg);
394 break;
395
396 case 'p':
397 pwd = malloc(strlen(optarg) + 1);
398 strcpy(pwd, optarg);
399 break;
400
401 case 'm':
402 for(int i = 0; map[i].name;i++ ) {
403 if (strcmp(optarg, map[i].name) == 0) {
404 mode = map[i].mode;
405 break;
406 }
407 }
408 break;
409
410 case 'v':
411 verbose_flag++;
412 break;
413
414 case '?':
415 break;
416
417 default:
418 abort();
419 }
420 }
421 }
422
423 // Loading config file if jid not provided by command line
424 if (jid == NULL) {
425 GKeyFile *config_file = g_key_file_new();
426 GError *error = NULL;
427 GString* configfile = g_string_new( g_get_home_dir());
428 g_string_append(configfile,"/.config/xmppc.conf");
429 gboolean configfilefound = g_key_file_load_from_file(
430 config_file,
431 configfile->str,
432 G_KEY_FILE_NONE,
433 &error);
434
435 if (!configfilefound) {
436 if(!g_error_matches(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
437 logError(&xmppc, "Error loading key file: %s", error->message);
438 return -1;
439 }
440 } else {
441 if(jid == NULL && pwd == NULL) {
442 logInfo(&xmppc,"Loading default account\n");
443 if( account == NULL ) {
444 account = "default";
445 }
446 jid = g_key_file_get_value (config_file, account, "jid" ,&error);
447 pwd = g_key_file_get_value (config_file, account, "pwd" ,&error);
448 }
449 }
450 }
451
452 if ( !pwd ) {
453 static struct termios current_terminal;
454 static struct termios pwd_terminal;
455
456 tcgetattr(STDIN_FILENO, ¤t_terminal);
457
458 pwd_terminal = current_terminal;
459 pwd_terminal.c_lflag &= ~(ECHO);
460 tcsetattr(STDIN_FILENO, TCSANOW, &pwd_terminal);
461 printf("Password for %s:", jid);
462 pwd = malloc(sizeof(char) * BUFSIZ);
463 if (fgets(pwd, BUFSIZ, stdin) == NULL) {
464 pwd[0] = '\0';
465 } else {
466 pwd[strlen(pwd)-1] = '\0';
467 }
468 tcsetattr(STDIN_FILENO, TCSANOW, ¤t_terminal);
469 printf("\n");
470 }
471
472 int paramc = argc- optind;
473 char* paramv[paramc];
474
475 for (int i = optind; i < argc; i++) {
476 char* x= malloc(strlen(argv[i])+1 *sizeof(char) );
477 strcpy(x,argv[i]);
478 paramv[i-optind] = x;
479 }
480 xmppc_context(&xmppc, verbose_flag);
481
482 logInfo(&xmppc, "Connecting %s ... ", jid);
483 xmppc_connect(&xmppc, jid, pwd);
484
485 ExecuteHandler handler = NULL;
486 for(int i = 0; map[i].name;i++ ) {
487 if (mode == map[i].mode) {
488 handler = map[i].callback;
489 break;
490 }
491 }
492
493 if( handler == NULL ) {
494 logError(&xmppc, "Unknown mode\n");
495 return -1;
496 }
497
498 callback_t callback = {paramc, paramv, handler, &xmppc};
499
500#if XMPPC_DEVELOPMENT
501 printf("Warning: Developer-Mode: XMPP_CONN_FLAG_TRUST_TLS\n");
502 xmpp_conn_set_flags(xmppc.conn, XMPP_CONN_FLAG_TRUST_TLS);
503#else
504 xmpp_conn_set_flags(xmppc.conn, XMPP_CONN_FLAG_MANDATORY_TLS);
505#endif
506
507 int e = xmpp_connect_client(xmppc.conn, NULL, 0, conn_handler, &callback);
508 if(XMPP_EOK != e ) {
509 printf("xmpp_connect_client failed");
510 }
511
512 xmpp_run(xmppc.ctx);
513 xmpp_conn_release(xmppc.conn);
514 xmpp_ctx_free(xmppc.ctx);
515 xmpp_shutdown();
516
517 free(jid);
518 free(pwd);
519
520 return EXIT_SUCCESS;
521}
522
523static void _show_help() {
524#ifdef XMPPC_DEVELOPMENT
525 printf("%s - Development\n", PACKAGE_STRING);
526#else
527 printf("%s\n", PACKAGE_STRING);
528#endif
529 printf("Usage: xmppc [--account <account>] [ --jid <jid> --pwd <pwd>] --mode <mode> <command> [<parameters> ...]\n");
530 printf("Options:\n");
531 printf(" -h / --help Display this information.\n");
532 printf(" -j / --jid <jid> Jabber ID\n");
533 printf(" -p / --pwd <password> Passwort\n");
534 printf(" -a / --account <account> Account\n");
535 printf(" -m / --mode <mode> xmppc mode\n");
536 printf("\n");
537 printf("Modes:\n");
538 printf(" -m --mode roster xmppc roster mode\n");
539 printf(" list List all contacts\n");
540 printf(" export Exports all contacts\n");
541 printf("\n");
542 printf(" -m --mode message xmppc message mode\n");
543 printf(" chat <jid> <message> Sending unencrypted message to jid\n");
544 printf("\n");
545 printf(" -m --mode pgp xmppc pgp mode (XEP-0027) \n");
546 printf(" chat <jid> <message> Sending pgp encrypted message to jid\n");
547 printf("\n");
548 printf(" -m --mode omemo xmppc omemo mode (XEP-0384)\n");
549 printf(" list List the device IDs and fingerprints\n");
550 printf("\n");
551 printf(" -m --mode openpgp xmppc openpgp mode (XEP-0373)\n");
552 printf(" signcrypt <jid> <message> Sending pgp signed and encrypted message to jid\n");
553 printf("\n");
554 printf(" -m --mode monitor Monitot mode\n");
555 printf(" stanza Stanza Monitor\n");
556 printf(" monitor microblog Monitor microblog (XEP-0277)\n");
557 printf("\n");
558 printf(" -m --mode bookmark Bookmark mode (XEP-0048)\n");
559 printf(" list List bookmarks\n");
560 printf("\n");
561 printf(" -m --mode mam Message Archive Management (XEP-0313)\n");
562 printf(" list <jid> List messages from <jid>\n");
563 printf("\n");
564 printf(" -m --mode discovery Service Discovery (XEP-0030)\n");
565 printf(" info <jid> info request for <jid>\n");
566 printf(" item <jid> item request for <jid>\n");
567 printf("\n");
568 printf("\n");
569 printf("Examples:\n");
570 printf(" Usage: xmppc --jid user@domain.tld --pwd \"secret\" --mode roster list\n");
571 printf(" Usage: xmppc --jid user@domain.tld --pwd \"secret\" --mode pgp chat friend@domain.tld \"Hello\"\n");
572 printf(" Usage: xmppc -a account1 --mode discovery item conference@domain.tld\n");
573 printf(" Usage: xmppc --mode bookmark list\n");
574}