main.c

  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  if(argc < 2) {
342    _show_help();
343    return EXIT_FAILURE;
344  }
345
346  INIT_XMPPC(xmppc);
347
348  static int verbose_flag = 0;
349  int c = 0;
350  xmppc_mode_t mode = UNKOWN;
351  char *jid = NULL;
352  char *pwd = NULL;
353  char *account = NULL;
354
355  static struct option long_options[] = {
356      /* These options set a flag. */
357      {"verbose", no_argument, &verbose_flag, 1},
358      {"help", no_argument, 0, 'h'},
359      {"config", required_argument, 0, 'c'},
360      {"account", required_argument, 0, 'a'},
361      {"jid", required_argument, 0, 'j'},
362      {"pwd", required_argument, 0, 'p'},
363      {"mode", required_argument, 0, 'm'},
364      {"file", required_argument, 0, 'f'},
365      {0, 0, 0, 0}};
366
367  // No arguments provided
368  if (argc == 1) {
369        _show_help();
370        return EXIT_SUCCESS;
371  }
372
373  while (c > -1) {
374    int option_index = 0;
375
376    c = getopt_long(argc, argv, "hva:j:p:m:", long_options, &option_index);
377    if (c > -1) {
378      switch (c) {
379      case 'h':
380        _show_help();
381        return EXIT_SUCCESS;
382
383      case 'c':
384        printf("option -c with value `%s'\n", optarg);
385        break;
386
387      case 'f':
388        printf("option -f with value `%s'\n", optarg);
389        break;
390
391      case 'a':
392        account = malloc(strlen(optarg) + 1);
393        strcpy(account, optarg);
394        break;
395
396      case 'j':
397        jid = malloc(strlen(optarg) + 1);
398        strcpy(jid, optarg);
399        break;
400
401      case 'p':
402        pwd = malloc(strlen(optarg) + 1);
403        strcpy(pwd, optarg);
404        break;
405
406      case 'm':
407        for(int i = 0; map[i].name;i++ ) {
408          if (strcmp(optarg, map[i].name) == 0) {
409            mode = map[i].mode;
410            break;
411          }
412        }
413        break;
414
415      case 'v':
416        verbose_flag++;
417        break;
418
419      case '?':
420        break;
421
422      default:
423        abort();
424      }
425    }
426  }
427
428  // Loading config file
429  GKeyFile *config_file = g_key_file_new();
430  GError *error = NULL;
431  GString* configfile = g_string_new( g_get_home_dir());
432  g_string_append(configfile,"/.config/xmppc.conf");
433  gboolean configfilefound = g_key_file_load_from_file(
434    config_file,
435    configfile->str,
436    G_KEY_FILE_NONE,
437    &error);
438
439  if (!configfilefound) {
440   if(error && !g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
441      logError(&xmppc, "Error loading key file: %s\n", error->message);
442      return EXIT_FAILURE;
443    }
444    if(jid == NULL && account == NULL) {
445      printf("You need either --jid or --account parameter or a default account\n");
446      _show_help();
447      return EXIT_FAILURE;
448    }
449  }
450  error = NULL;
451
452  if(jid == NULL && pwd == NULL) {
453    logInfo(&xmppc,"Loading default account\n");
454    if( account == NULL ) {
455      account = "default";
456    }
457    jid = g_key_file_get_value (config_file, account, "jid" ,&error);
458    if( error ) {
459      logError(&xmppc, "No jid found in configuration file. %s\n", error->message);
460      return EXIT_FAILURE;
461    }
462    pwd = g_key_file_get_value (config_file, account, "pwd" ,&error);
463  }
464
465  if ( !pwd ) {
466    static struct termios current_terminal;
467    static struct termios pwd_terminal;
468
469    tcgetattr(STDIN_FILENO, &current_terminal);
470
471    pwd_terminal = current_terminal;
472    pwd_terminal.c_lflag &= ~(ECHO);
473    tcsetattr(STDIN_FILENO, TCSANOW, &pwd_terminal);
474    printf("Password for %s:", jid);
475    pwd = malloc(sizeof(char) * BUFSIZ);
476    if (fgets(pwd, BUFSIZ, stdin) == NULL) {
477        pwd[0] = '\0';
478    } else {
479        pwd[strlen(pwd)-1] = '\0';
480    }
481    tcsetattr(STDIN_FILENO, TCSANOW, &current_terminal);
482    printf("\n");
483  }
484
485  int paramc = argc- optind;
486  char* paramv[paramc];
487
488  for (int i = optind; i < argc; i++) {
489    char* x= malloc(strlen(argv[i])+1 *sizeof(char)  );
490    strcpy(x,argv[i]);
491    paramv[i-optind] = x;
492  }
493  xmppc_context(&xmppc, verbose_flag);
494
495  logInfo(&xmppc, "Connecting %s ... ", jid);
496  xmppc_connect(&xmppc, jid, pwd);
497
498  ExecuteHandler handler = NULL;
499        for(int i = 0; map[i].name;i++ ) {
500          if (mode ==  map[i].mode) {
501            handler = map[i].callback;
502            break;
503          }
504        }
505
506  if( handler == NULL ) {
507    logError(&xmppc, "Unknown mode\n");
508    return -1;
509  } 
510
511  callback_t callback = {paramc, paramv, handler, &xmppc};
512
513#if XMPPC_DEVELOPMENT
514  printf("Warning: Developer-Mode: XMPP_CONN_FLAG_TRUST_TLS\n");
515  xmpp_conn_set_flags(xmppc.conn, XMPP_CONN_FLAG_TRUST_TLS);
516#else
517  xmpp_conn_set_flags(xmppc.conn, XMPP_CONN_FLAG_MANDATORY_TLS);
518#endif
519
520  int e =  xmpp_connect_client(xmppc.conn, NULL, 0, conn_handler, &callback);
521  if(XMPP_EOK != e ) {
522    printf("xmpp_connect_client failed");
523  }
524
525  xmpp_run(xmppc.ctx);
526  xmpp_conn_release(xmppc.conn);
527  xmpp_ctx_free(xmppc.ctx);
528  xmpp_shutdown();
529
530  free(jid);
531  free(pwd);
532
533  return EXIT_SUCCESS;
534}
535
536static void _show_help() {
537#ifdef XMPPC_DEVELOPMENT
538  printf("%s - Development\n", PACKAGE_STRING);
539#else
540  printf("%s\n", PACKAGE_STRING);
541#endif
542  printf("Usage: xmppc [--account <account>] [ --jid <jid> --pwd <pwd>] --mode <mode> <command> [<parameters> ...]\n");
543  printf("Options:\n");
544  printf("  -h / --help                 Display this information.\n");
545  printf("  -j / --jid <jid>            Jabber ID\n");
546  printf("  -p / --pwd <password>       Passwort\n");
547  printf("  -a / --account <account>    Account\n");
548  printf("  -m / --mode <mode>          xmppc mode\n");
549  printf("\n");
550  printf("Modes:\n");
551  printf("  -m --mode roster            xmppc roster mode\n");
552  printf("    list                      List all contacts\n");
553  printf("    export                    Exports all contacts\n");
554  printf("\n");
555  printf("  -m --mode message           xmppc message mode\n");
556  printf("    chat <jid> <message>      Sending unencrypted message to jid\n");
557  printf("\n");
558  printf("  -m --mode pgp               xmppc pgp mode (XEP-0027) \n");
559  printf("    chat <jid> <message>      Sending pgp encrypted message to jid\n");
560  printf("\n");
561  printf("  -m --mode omemo             xmppc omemo mode (XEP-0384)\n");
562  printf("    list                      List the device IDs and fingerprints\n");
563  printf("    delete-device-list        Delete OMEMO device list\n");
564  printf("\n");
565  printf("  -m --mode openpgp           xmppc openpgp mode (XEP-0373)\n");
566  printf("    signcrypt <jid> <message> Sending pgp signed and encrypted message to jid\n");
567  printf("\n");
568  printf("  -m --mode monitor           Monitot mode\n");
569  printf("    stanza                    Stanza Monitor\n");
570  printf("    monitor                   microblog Monitor microblog (XEP-0277)\n");
571  printf("\n");
572  printf("  -m --mode bookmark          Bookmark mode (XEP-0048)\n");
573  printf("    list                      List bookmarks\n");
574  printf("\n");
575  printf("  -m --mode mam               Message Archive Management (XEP-0313)\n");
576  printf("    list <jid>                List messages from <jid>\n");
577  printf("\n");
578  printf("  -m --mode discovery         Service Discovery (XEP-0030)\n");
579  printf("    info <jid>                info request for <jid>\n");
580  printf("    item <jid>                item request for <jid>\n");
581  printf("\n");
582  printf("\n");
583  printf("Examples:\n");
584  printf("  Usage: xmppc --jid user@domain.tld --pwd \"secret\" --mode roster list\n");
585  printf("  Usage: xmppc --jid user@domain.tld --pwd \"secret\" --mode pgp chat friend@domain.tld \"Hello\"\n");
586  printf("  Usage: xmppc -a account1 --mode discovery item conference@domain.tld\n");
587  printf("  Usage: xmppc --mode bookmark list\n");
588}