static void
	report_revision (bool found_revision)
{
	char const *rev = quotearg (revision);

	if (found_revision)
	{
		if (verbosity == VERBOSE)
			say ("Good.  This file appears to be the %s version.\n", rev);
	}
	else if (force)
	{
		if (verbosity != SILENT)
			say ("Warning: this file doesn't appear to be the %s version -- patching anyway.\n",
			rev);
	}
	else if (batch)
		fatal ("This file doesn't appear to be the %s version -- aborting.",
		rev);
	else
	{
		ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ",
			rev);
		if (*buf != 'y')
			fatal ("aborted");
	}
}
Esempio n. 2
0
static char *
use_quotearg (const char *str, size_t *len)
{
  char *p = *len == SIZE_MAX ? quotearg (str) : quotearg_mem (str, *len);
  *len = strlen (p);
  return p;
}
Esempio n. 3
0
static void
var_set_listen_addr(DCVariable *var, int argc, char **argv)
{
    struct sockaddr_in addr;

    if (argc > 2) {
        warn(_("too many arguments\n"));
        return;
    }
    if (argv[1][0] == '\0') {
        force_listen_addr.s_addr = INADDR_NONE;
        screen_putf(_("Removing listening address.\n"));
        return;
    }

    if (!inet_aton(argv[1], &addr.sin_addr)) {
        screen_putf(_("%s: Specify listen address as an IP address\n"), quotearg(argv[1]));
        /* XXX: fix this in the future... */
	/*struct hostent *he;

	screen_putf(_("Looking up IP address for %s\n"), quotearg(argv[1]));
	he = gethostbyname(argv[1]);
	if (he == NULL) {
	    screen_putf(_("%s: Cannot look up address - %s\n"), quotearg(argv[1]), hstrerror(h_errno));
	    return;
	}

	addr.sin_addr = *(struct in_addr *) he->h_addr;*/
    }

    force_listen_addr = addr.sin_addr;
    screen_putf(_("Listening address set to %s.\n"), inet_ntoa(force_listen_addr));
}
Esempio n. 4
0
/* Create FILE with OPEN_FLAGS, and with MODE adjusted so that
   we can read and write the file and that the file is not executable.
   Return the file descriptor.  */
int
create_file (char const *file, int open_flags, mode_t mode,
	     bool to_dir_known_to_exist)
{
  int try_makedirs_errno = to_dir_known_to_exist ? 0 : ENOENT;
  int fd;
  mode |= S_IRUSR | S_IWUSR;
  mode &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
  do
    {
      if (! (O_CREAT && O_TRUNC))
	close (creat (file, mode));
      fd = open (file, O_CREAT | O_TRUNC | open_flags, mode);
      if (fd < 0)
	{
	  char *f;
	  if (errno != try_makedirs_errno)
	    pfatal ("Can't create file %s", quotearg (file));
	  f = xstrdup (file);
	  makedirs (f);
	  free (f);
	  try_makedirs_errno = 0;
	}
    } while (fd < 0);
  return fd;
}
Esempio n. 5
0
void
cmd_set(int argc, char **argv)
{
    uint32_t c;
    DCVariable *var;

    if (argc == 1) {
	int max_len = 0;
	int cols;
	char *fmt;
	
	for (c = 0; c < variables_count; c++)
	    max_len = MAX(max_len, strlen(variables[c].name));
	fmt = xasprintf("%%-%ds  %%s\n", max_len);
	screen_get_size(NULL, &cols);
	for (c = 0; c < variables_count; c++) {
	    DCVariable *var = &variables[c];
	    char *value = var->getter(var);

	    screen_putf(fmt, var->name, value == NULL ? "(unset)" : quotearg(value));
	    free(value);
	}
	free(fmt);
	return;
    }

    var = find_variable(argv[1]);
    if (var == NULL) {
	warn("No variable by the name `%s'.\n", quotearg(argv[1]));
	return;
    }

    if (argc <= 2) {
	char *value;
	value = var->getter(var);
	if (value == NULL) {
	    screen_putf(_("No value is set for `%s'.\n"), var->name);
	} else {
	    screen_putf(_("Current value for `%s':\n%s\n"), var->name, quotearg(value));
	}
	return;
    }

    var->setter(var, argc-1, argv+1);
}
Esempio n. 6
0
/* This function tries to make a connection to a user, or ask them to
 * connect to us.
 *
 * Before calling this function, make sure we are not connected to the
 * user already.
 *
 * This function makes sure an unanswerred (Rev)ConnectToMe hasn't been
 * sent previously.
 *
 * This function is the only place that is allowed to send $ConnectToMe.
 * $RevConnectToMe may be set by one other place (when $RevConnectToMe
 * was received and we did not previously send $RevConnectToMe).
 */
bool
hub_connect_user(DCUserInfo *ui)
{
    char *hub_my_nick;
    char *hub_ui_nick;
    bool connect = false;

    hub_my_nick = main_to_hub_string(my_nick);
    hub_ui_nick = main_to_hub_string(ui->nick);

    if (is_active) {
        if (ui->active_state == DC_ACTIVE_SENT_ACTIVE) {
            warn(_("ConnectToMe already sent to user %s. Waiting.\n"), ui->nick);
            connect =  true;
            goto cleanup;
        }
        if (!hub_putf("$ConnectToMe %s %s:%u|", hub_ui_nick, inet_ntoa(local_addr.sin_addr), listen_port))
            goto cleanup;
        ui->active_state = DC_ACTIVE_SENT_ACTIVE;
    } else {
        if (ui->active_state == DC_ACTIVE_SENT_PASSIVE) {
            warn(_("RevConnectToMe already sent to user %s. Waiting.\n"), quotearg(ui->nick));
            connect =  true;
            goto cleanup;
        }
        if (ui->active_state == DC_ACTIVE_RECEIVED_PASSIVE) {
            warn(_("User %s is also passive. Cannot communicate.\n"), quotearg(ui->nick));
            connect =  true;
            goto cleanup;
        }
        if (!hub_putf("$RevConnectToMe %s %s|", hub_my_nick, hub_ui_nick)) {
            goto cleanup;
        }
        ui->active_state = DC_ACTIVE_SENT_PASSIVE;
    }
    /* hmap_put returns the old value */
    if (hmap_put(pending_userinfo, ui->nick, ui) == NULL)
        ui->refcount++;
    connect = true;
cleanup:
    free(hub_ui_nick);
    free(hub_my_nick);
    return connect;
}
Esempio n. 7
0
void
uniqstr_assert (char const *str)
{
  if (!hash_lookup (uniqstrs_table, str))
    {
      error (0, 0,
             "not a uniqstr: %s", quotearg (str));
      abort ();
    }
}
Esempio n. 8
0
/* Open a stream for writing on the file FILENAME, making, if
   necessary and adequate, a backup according the policy BACKUP_TYPE */
FILE *
fopen_backup (const char * filename, enum backup_type backup_type)
{
  char * backup_name = NULL;
  struct stat filestat;
  FILE * res;

  /* No backup upon non existing files */
  if (stat (filename, &filestat))
    {
      if ((errno == ENOENT)
	  || (errno == ENOTDIR))
	/* the file does not exist: return */
	backup_type = none ;
      else
	/* Another kind of error occured: exit */
	error (1, errno, _("cannot get informations on file `%s'"),
	       quotearg (filename));
    }

  /* If the file is special (/dev/null etc.), don't backup.
     Or if we don't have the rights to open the file for writing, don't
     backup, so that the forthcoming fopen does complain on the rights*/
  if (!S_ISREG (filestat.st_mode)
      || access (filename, W_OK))
    backup_type = none ;

  /* Definitely, make a backup */
  if (backup_type != none)
    {
      backup_name = xfind_backup_file_name (filename, backup_type);
      if (rename (filename, backup_name))
	error (1, errno, _("cannot rename file `%s' as `%s'"),
	       quotearg (filename), quotearg (backup_name));
    }

  /* Open the file for reading */
  res = fopen (filename, "w");
  if (!res)
    {
      error (0, errno, _("cannot create file `%s'"), quotearg (filename));
      if (backup_name)
	{
	  if (rename (filename, backup_name))
	    error (0, errno, _("cannot rename file `%s' as `%s'"),
		   quotearg (filename), quotearg (backup_name));
	  else
	    fprintf (stderr, _("restored file `%s'"), quotearg (filename));
	}
      exit (EXIT_FAILURE);
    }

  if (backup_name)
    free (backup_name);

  return res;
}
Esempio n. 9
0
static void
var_set_download_dir(DCVariable *var, int argc, char **argv)
{
    struct stat st;

    if (argc > 2) {
	warn(_("too many arguments\n"));
	return;
    }
    if (stat(argv[1], &st) < 0) {
    	screen_putf(_("%s: Cannot get file status - %s\n"), quotearg(argv[1]), errstr);
	return;
    }
    if (!S_ISDIR(st.st_mode)) {
    	screen_putf(_("%s: Not a directory\n"), quotearg(argv[1]));
	return;
    }
    free(download_dir);
    download_dir = xstrdup(argv[1]);
}
Esempio n. 10
0
void
append_to_file (char const *from, char const *to)
{
  int tofd;

  if ((tofd = open (to, O_WRONLY | O_BINARY | O_APPEND)) < 0)
    pfatal ("Can't reopen file %s", quotearg (to));
  copy_to_fd (from, tofd);
  if (close (tofd) != 0)
    write_fatal ();
}
Esempio n. 11
0
/* Create FILE with OPEN_FLAGS, and with MODE adjusted so that
   we can read and write the file and that the file is not executable.
   Return the file descriptor.  */
int
create_file (char const *file, int open_flags, mode_t mode)
{
  int fd;
  mode |= S_IRUSR | S_IWUSR;
  mode &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
  if (! (O_CREAT && O_TRUNC))
    close (creat (file, mode));
  fd = open (file, O_CREAT | O_TRUNC | open_flags, mode);
  if (fd < 0)
    pfatal ("Can't create file %s", quotearg (file));
  return fd;
}
Esempio n. 12
0
  /* Open the file for reading */
  res = fopen (filename, "w");
  if (!res)
    {
      error (0, errno, _("cannot create file `%s'"), quotearg (filename));
      if (backup_name)
	{
	  if (rename (filename, backup_name))
	    error (0, errno, _("cannot rename file `%s' as `%s'"),
		   quotearg (filename), quotearg (backup_name));
	  else
	    fprintf (stderr, _("restored file `%s'"), quotearg (filename));
	}
      exit (EXIT_FAILURE);
    }

  if (backup_name)
    free (backup_name);

  return res;
}
#else /* !USE_OLD_FOPEN_BACKUP */
FILE *
fopen_backup (const char * filename, enum backup_type backup_type)
{
  FILE * res;
  int fd;

  fd = create_file_for_backup (filename, O_CREAT, 0666, backup_type);
  if (fd < 0)
    {
      if (backup_type == none)
	error (1, errno, _("cannot create file `%s'"), quotearg (filename));
      else
	error (1, errno, ("cannot backup and create file `%s'"),
	       quotearg (filename));
    }

  res = fdopen (fd, "w");
  if (!res)
    error (1, errno, _("cannot create file `%s'"), quotearg (filename));

  return res;
}
Esempio n. 13
0
static void
var_set_slots(DCVariable *var, int argc, char **argv)
{
    if (argc > 2) {
	warn(_("too many arguments\n"));
	return;
    }
    if (!parse_uint32(argv[1], &my_ul_slots))
    	screen_putf(_("Invalid slot number `%s'\n"), quotearg(argv[1]));

    if (hub_state >= DC_HUB_LOGGED_IN)
        send_my_info();
}
Esempio n. 14
0
static bool
validate_nick(DCUserConn *uc, const char *nick)
{
    DCUserInfo *ui;

    ui = (DCUserInfo*) hmap_remove(pending_userinfo, nick);
    if (ui != NULL) {
        if (ui->conn_count < DC_USER_MAX_CONN) {
            uc->info = ui;
            uc->info->refcount++;
            uc->info->conn[uc->info->conn_count++] = uc;
            hmap_remove(pending_userinfo, ui->nick);
            user_info_free(ui);
            update_user_connection_name(uc, "%s|", uc->info->nick);
            return true;
        }
        warn(_("No more connections to user %s allowed.\n"), quotearg(nick));
        user_info_free(ui);
        return false;
    }

    ui = (DCUserInfo*) hmap_get(hub_users, nick);
    if (ui != NULL) {
        if (ui->conn_count < DC_USER_MAX_CONN) {
            uc->info = ui;
            uc->info->refcount++;
            uc->info->conn[uc->info->conn_count++] = uc;
            update_user_connection_name(uc, "%s|", uc->info->nick);
            return true;
        }
        warn(_("No more connections to user %s allowed.\n"), quotearg(nick));
        return false;
    }

    return false;
}
Esempio n. 15
0
static void
hub_address_looked_up(int rc, struct addrinfo *ai, void *data)
{
    char *hostname = (char*) data;

    hub_lookup = NULL;
    if (rc != 0) {
        screen_putf(_("%s: Cannot look up address - %s\n"), quotearg(hostname), gai_strerror(rc));
        free(data);
        return;
    }

    hub_set_connected(true);
    hub_connect((struct sockaddr_in *) ai->ai_addr);
    free(data);
}
Esempio n. 16
0
static void
var_set_display_flags(DCVariable *var, int argc, char **argv)
{
    uint32_t add_values = 0;
    uint32_t set_values = 0;
    uint32_t del_values = 0;
    uint32_t c;

    for (c = 1; c < argc; c++) {
	char *arg = argv[c];
	uint32_t *values;
	uint32_t value;

	if (arg[0] == '+') {
	    values = &add_values;
	    arg++;
	} else if (arg[0] == '-') {
	    values = &del_values;
	    arg++;
	} else {
	    values = &set_values;
	}
	if (strcmp(arg, "all") == 0) {
	    value = ~0;
	} else if (strcmp(arg, "default") == 0) {
	    value = ~0;
	} else {
	    value = find_display_flag_value(arg);
	}
	if (value == 0) {
	    screen_putf(_("No flag by the name %s, display flags not changed.\n"), quotearg(arg));
	    return;
	}
	*values |= value;
    }

    if (set_values != 0 && (add_values != 0 || del_values != 0))
	screen_putf(_("Cannot set and add or delete flags at the same time.\n"));

    if (set_values != 0)
	*((uint32_t *) var->value) = set_values;
    *((uint32_t *) var->value) |= add_values;
    *((uint32_t *) var->value) &= ~del_values;
    *((uint32_t *) var->value) |= DC_DF_COMMON;	/* XXX: must always be available, but for logging? */
}
Esempio n. 17
0
static void
copy_to_fd (const char *from, int tofd)
{
  int fromfd;
  ssize_t i;

  if ((fromfd = open (from, O_RDONLY | O_BINARY)) < 0)
    pfatal ("Can't reopen file %s", quotearg (from));
  while ((i = read (fromfd, buf, bufsize)) != 0)
    {
      if (i == (ssize_t) -1)
	read_fatal ();
      if (full_write (tofd, buf, i) != i)
	write_fatal ();
    }
  if (close (fromfd) != 0)
    read_fatal ();
}
Esempio n. 18
0
void
	scan_input (char *filename)
{
	using_plan_a = ! (debug & 16) && plan_a (filename);
	if (!using_plan_a)
		plan_b(filename);

	if (verbosity != SILENT)
	{
		filename = quotearg (filename);

		if (verbosity == VERBOSE)
			say ("Patching file %s using Plan %s...\n",
			filename, using_plan_a ? "A" : "B");
		else
			say ("patching file %s\n", filename);
	}
}
Esempio n. 19
0
/* port must be valid */
void
hub_new(const char *hostname, uint16_t port)
{
    struct sockaddr_in addr;

    if (inet_aton(hostname, &addr.sin_addr)) {
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        hub_set_connected(true);
        hub_connect(&addr); /* Ignore errors */
    } else {
        char portstr[6];

        sprintf(portstr, "%" PRIu16, port);
        screen_putf(_("Looking up IP address for %s\n"), quotearg(hostname));
        hub_lookup = add_lookup_request(hostname, portstr, NULL, hub_address_looked_up, xstrdup(hostname));
        hub_state = DC_HUB_LOOKUP;
    }
}
Esempio n. 20
0
static void
var_set_listen_port(DCVariable *var, int argc, char **argv)
{
    uint16_t port;
    if (argc > 2) {
	warn(_("too many arguments\n"));
	return;
    }
    if (argv[1][0] == '\0') {
	port = 0;
    } else {
	if (!parse_uint16(argv[1], &port)) {
	    screen_putf(_("Invalid value `%s' for port number.\n"), quotearg(argv[1]));
	    return;
	}
    }
    if (!set_active(is_active, port))
    	screen_putf(_("Active setting not changed.\n"));
}
Esempio n. 21
0
static void
var_set_filelist_refresh_interval(DCVariable *var, int argc, char **argv)
{
    unsigned int interval;
    if (argc > 2) {
        warn(_("too many arguments\n"));
        return;
    }
    if (argv[1][0] == '\0') {
        interval = 0;
    } else {
        if (!parse_uint32(argv[1], &interval)) {
            screen_putf(_("Invalid value `%s' for interval.\n"), quotearg(argv[1]));
            return;
        }
    }
    filelist_refresh_timeout = interval;

    update_request_set_filelist_refresh_timeout(filelist_refresh_timeout);
}
Esempio n. 22
0
/* Remove empty ancestor directories of FILENAME.
   Ignore errors, since the path may contain ".."s, and when there
   is an EEXIST failure the system may return some other error number.  */
void
removedirs (char *filename)
{
  size_t i;

  for (i = strlen (filename);  i != 0;  i--)
    if (ISSLASH (filename[i])
	&& ! (ISSLASH (filename[i - 1])
	      || (filename[i - 1] == '.'
		  && (i == 1
		      || ISSLASH (filename[i - 2])
		      || (filename[i - 2] == '.'
			  && (i == 2
			      || ISSLASH (filename[i - 3])))))))
      {
	filename[i] = '\0';
	if (rmdir (filename) == 0 && verbosity == VERBOSE)
	  say ("Removed empty directory %s\n", quotearg (filename));
	filename[i] = '/';
      }
}
Esempio n. 23
0
void
copy_file (char const *from, char const *to, int to_flags, mode_t mode)
{
  int tofd;
  int fromfd;
  size_t i;

  if ((fromfd = open (from, O_RDONLY | O_BINARY, 0)) < 0)
    pfatal ("Can't reopen file %s", quotearg (from));
  tofd = create_file (to, O_WRONLY | O_BINARY | to_flags, mode);
  while ((i = read (fromfd, buf, bufsize)) != 0)
    {
      if (i == (size_t) -1)
	read_fatal ();
      if (write (tofd, buf, i) != i)
	write_fatal ();
    }
  if (close (fromfd) != 0)
    read_fatal ();
  if (close (tofd) != 0)
    write_fatal ();
}
Esempio n. 24
0
void
move_file (char const *from, bool *from_needs_removal,
	   struct stat const *fromst,
	   char const *to, mode_t mode, bool backup)
{
  struct stat to_st;
  int to_errno;

  to_errno = stat_file (to, &to_st);
  if (backup)
    create_backup (to, to_errno ? NULL : &to_st, false);
  if (! to_errno)
    insert_file_id (&to_st, OVERWRITTEN);

  if (from)
    {
      if (S_ISLNK (mode))
	{
	  bool to_dir_known_to_exist = false;

	  /* FROM contains the contents of the symlink we have patched; need
	     to convert that back into a symlink. */
	  char *buffer = xmalloc (PATH_MAX);
	  int fd, size = 0, i;

	  if ((fd = open (from, O_RDONLY | O_BINARY)) < 0)
	    pfatal ("Can't reopen file %s", quotearg (from));
	  while ((i = read (fd, buffer + size, PATH_MAX - size)) > 0)
	    size += i;
	  if (i != 0 || close (fd) != 0)
	    read_fatal ();
	  buffer[size] = 0;

	  if (! backup)
	    {
	      if (unlink (to) == 0)
		to_dir_known_to_exist = true;
	    }
	  if (symlink (buffer, to) != 0)
	    {
	      if (errno == ENOENT && ! to_dir_known_to_exist)
		makedirs (to);
	      if (symlink (buffer, to) != 0)
		pfatal ("Can't create %s %s", "symbolic link", to);
	    }
	  free (buffer);
	  if (lstat (to, &to_st) != 0)
	    pfatal ("Can't get file attributes of %s %s", "symbolic link", to);
	  insert_file_id (&to_st, CREATED);
	}
      else
	{
	  if (debug & 4)
	    say ("Renaming file %s to %s\n",
		 quotearg_n (0, from), quotearg_n (1, to));

	  if (rename (from, to) != 0)
	    {
	      bool to_dir_known_to_exist = false;

	      if (errno == ENOENT
		  && (to_errno == -1 || to_errno == ENOENT))
		{
		  makedirs (to);
		  to_dir_known_to_exist = true;
		  if (rename (from, to) == 0)
		    goto rename_succeeded;
		}

	      if (errno == EXDEV)
		{
		  struct stat tost;
		  if (! backup)
		    {
		      if (unlink (to) == 0)
			to_dir_known_to_exist = true;
		      else if (errno != ENOENT)
			pfatal ("Can't remove file %s", quotearg (to));
		    }
		  copy_file (from, to, &tost, 0, mode, to_dir_known_to_exist);
		  insert_file_id (&tost, CREATED);
		  return;
		}

	      pfatal ("Can't rename file %s to %s",
		      quotearg_n (0, from), quotearg_n (1, to));
	    }

	rename_succeeded:
	  insert_file_id (fromst, CREATED);
	  /* Do not clear *FROM_NEEDS_REMOVAL if it's possible that the
	     rename returned zero because FROM and TO are hard links to
	     the same file.  */
	  if ((0 < to_errno
	       || (to_errno == 0 && to_st.st_nlink <= 1))
	      && from_needs_removal)
	    *from_needs_removal = false;
	}
    }
  else if (! backup)
    {
      if (debug & 4)
	say ("Removing file %s\n", quotearg (to));
      if (unlink (to) != 0 && errno != ENOENT)
	pfatal ("Can't remove file %s", quotearg (to));
    }
}
Esempio n. 25
0
void
create_backup (char const *to, const struct stat *to_st, bool leave_original)
{
  /* When the input to patch modifies the same file more than once, patch only
     backs up the initial version of each file.

     To figure out which files have already been backed up, patch remembers the
     files that replace the original files.  Files not known already are backed
     up; files already known have already been backed up before, and are
     skipped.

     When a patch tries to delete a file, in order to not break the above
     logic, we merely remember which file to delete.  After the entire patch
     file has been read, we delete all files marked for deletion which have not
     been recreated in the meantime.  */

  if (to_st && ! (S_ISREG (to_st->st_mode) || S_ISLNK (to_st->st_mode)))
    fatal ("File %s is not a %s -- refusing to create backup",
	   to, S_ISLNK (to_st->st_mode) ? "symbolic link" : "regular file");

  if (to_st && lookup_file_id (to_st) == CREATED)
    {
      if (debug & 4)
	say ("File %s already seen\n", quotearg (to));
    }
  else
    {
      int try_makedirs_errno = 0;
      char *bakname;

      if (origprae || origbase || origsuff)
	{
	  char const *p = origprae ? origprae : "";
	  char const *b = origbase ? origbase : "";
	  char const *s = origsuff ? origsuff : "";
	  char const *t = to;
	  size_t plen = strlen (p);
	  size_t blen = strlen (b);
	  size_t slen = strlen (s);
	  size_t tlen = strlen (t);
	  char const *o;
	  size_t olen;

	  for (o = t + tlen, olen = 0;
	       o > t && ! ISSLASH (*(o - 1));
	       o--)
	    /* do nothing */ ;
	  olen = t + tlen - o;
	  tlen -= olen;
	  bakname = xmalloc (plen + tlen + blen + olen + slen + 1);
	  memcpy (bakname, p, plen);
	  memcpy (bakname + plen, t, tlen);
	  memcpy (bakname + plen + tlen, b, blen);
	  memcpy (bakname + plen + tlen + blen, o, olen);
	  memcpy (bakname + plen + tlen + blen + olen, s, slen + 1);

	  if ((origprae
	       && (contains_slash (origprae + FILE_SYSTEM_PREFIX_LEN (origprae))
		   || contains_slash (to)))
	      || (origbase && contains_slash (origbase)))
	    try_makedirs_errno = ENOENT;
	}
      else
	{
	  bakname = find_backup_file_name (to, backup_type);
	  if (!bakname)
	    xalloc_die ();
	}

      if (! to_st)
	{
	  int fd;

	  if (debug & 4)
	    say ("Creating empty file %s\n", quotearg (bakname));

	  try_makedirs_errno = ENOENT;
	  unlink (bakname);
	  while ((fd = creat (bakname, 0666)) < 0)
	    {
	      if (errno != try_makedirs_errno)
		pfatal ("Can't create file %s", quotearg (bakname));
	      makedirs (bakname);
	      try_makedirs_errno = 0;
	    }
	  if (close (fd) != 0)
	    pfatal ("Can't close file %s", quotearg (bakname));
	}
      else if (leave_original)
	create_backup_copy (to, bakname, to_st, try_makedirs_errno == 0);
      else
	{
	  if (debug & 4)
	    say ("Renaming file %s to %s\n",
		 quotearg_n (0, to), quotearg_n (1, bakname));
	  while (rename (to, bakname) != 0)
	    {
	      if (errno == try_makedirs_errno)
		{
		  makedirs (bakname);
		  try_makedirs_errno = 0;
		}
	      else if (errno == EXDEV)
		{
		  create_backup_copy (to, bakname, to_st,
				      try_makedirs_errno == 0);
		  unlink (to);
		  break;
		}
	      else
		pfatal ("Can't rename file %s to %s",
			quotearg_n (0, to), quotearg_n (1, bakname));
	    }
	}
      free (bakname);
    }
}
Esempio n. 26
0
void
set_file_attributes (char const *to, enum file_attributes attr,
		     char const *from, const struct stat *st, mode_t mode,
		     struct timespec *new_time)
{
  if (attr & FA_TIMES)
    {
      struct timespec times[2];
      if (new_time)
	times[0] = times[1] = *new_time;
      else
        {
	  times[0] = get_stat_atime (st);
	  times[1] = get_stat_mtime (st);
	}
      if (lutimens (to, times) != 0)
	pfatal ("Failed to set the timestamps of %s %s",
		S_ISLNK (mode) ? "symbolic link" : "file",
		quotearg (to));
    }
  if (attr & FA_IDS)
    {
      static uid_t euid = -1;
      static gid_t egid = -1;
      uid_t uid;
      uid_t gid;

      if (euid == -1)
        {
	  euid = geteuid ();
	  egid = getegid ();
	}
      uid = (euid == st->st_uid) ? -1 : st->st_uid;
      gid = (egid == st->st_gid) ? -1 : st->st_gid;

      /* May fail if we are not privileged to set the file owner, or we are
         not in group instat.st_gid.  Ignore those errors.  */
      if ((uid != -1 || gid != -1)
	  && lchown (to, uid, gid) != 0
	  && (errno != EPERM
	      || (uid != -1
		  && lchown (to, (uid = -1), gid) != 0
		  && errno != EPERM)))
	pfatal ("Failed to set the %s of %s %s",
		(uid == -1) ? "owner" : "owning group",
		S_ISLNK (mode) ? "symbolic link" : "file",
		quotearg (to));
    }
  if (attr & FA_XATTRS)
    if (copy_attr (from, to) != 0
	&& errno != ENOSYS && errno != ENOTSUP && errno != EPERM)
      fatal_exit (0);
  if (attr & FA_MODE)
    {
#if 0 && defined HAVE_LCHMOD
      /* The "diff --git" format does not store the file permissions of
	 symlinks, so don't try to set symlink file permissions even on
	 systems where we could.  */
      if (lchmod (to, mode))
#else
      if (! S_ISLNK (mode) && chmod (to, mode) != 0)
#endif
	pfatal ("Failed to set the permissions of %s %s",
		S_ISLNK (mode) ? "symbolic link" : "file",
		quotearg (to));
    }
}
Esempio n. 27
0
static char const *
copy_attr_quote (struct error_context *ctx, char const *str)
{
  return quotearg (str);
}
Esempio n. 28
0
void
fetchname (char const *at, int strip_leading, char **pname,
	   char **ptimestr, struct timespec *pstamp)
{
    char *name;
    const char *t;
    char *timestr = NULL;
    struct timespec stamp;

    stamp.tv_sec = -1;
    stamp.tv_nsec = 0;

    while (ISSPACE ((unsigned char) *at))
	at++;
    if (debug & 128)
	say ("fetchname %s %d\n", at, strip_leading);

    if (*at == '"')
      {
	name = parse_c_string (at, &t);
	if (! name)
	  {
	    if (debug & 128)
	      say ("ignoring malformed filename %s\n", quotearg (at));
	    return;
	  }
      }
    else
      {
	for (t = at;  *t;  t++)
	  {
	    if (ISSPACE ((unsigned char) *t))
	      {
		/* Allow file names with internal spaces,
		   but only if a tab separates the file name from the date.  */
		char const *u = t;
		while (*u != '\t' && ISSPACE ((unsigned char) u[1]))
		  u++;
		if (*u != '\t' && (strchr (u + 1, pstamp ? '\t' : '\n')))
		  continue;
		break;
	      }
	  }
	name = savebuf (at, t - at + 1);
	name[t - at] = 0;
      }

    /* If the name is "/dev/null", ignore the name and mark the file
       as being nonexistent.  The name "/dev/null" appears in patches
       regardless of how NULL_DEVICE is spelled.  */
    if (strcmp (name, "/dev/null") == 0)
      {
	free (name);
	if (pstamp)
	  {
	    pstamp->tv_sec = 0;
	    pstamp->tv_nsec = 0;
	  }
	return;
      }

    /* Ignore the name if it doesn't have enough slashes to strip off.  */
    if (! strip_leading_slashes (name, strip_leading))
      {
	free (name);
	return;
      }

    if (ptimestr)
      {
	char const *u = t + strlen (t);

	if (u != t && *(u-1) == '\n')
	  u--;
	if (u != t && *(u-1) == '\r')
	  u--;
	timestr = savebuf (t, u - t + 1);
	timestr[u - t] = 0;
      }

      if (*t != '\n')
	{
	  if (! pstamp)
	    {
	      free (name);
	      free (timestr);
	      return;
	    }

	  if (set_time | set_utc)
	    get_date (&stamp, t, &initial_time);
	  else
	    {
	      /* The head says the file is nonexistent if the
		 timestamp is the epoch; but the listed time is
		 local time, not UTC, and POSIX.1 allows local
		 time offset anywhere in the range -25:00 <
		 offset < +26:00.  Match any time in that range.  */
	      const struct timespec lower = { -25L * 60 * 60 },
				    upper = {  26L * 60 * 60 };
	      if (get_date (&stamp, t, &initial_time)
		  && timespec_cmp (stamp, lower) > 0
		  && timespec_cmp (stamp, upper) < 0) {
		      stamp.tv_sec = 0;
		      stamp.tv_nsec = 0;
	      }
	    }
	}

    free (*pname);
    *pname = name;
    if (ptimestr)
      {
	free (*ptimestr);
	*ptimestr = timestr;
      }
    if (pstamp)
      *pstamp = stamp;
}
Esempio n. 29
0
void
move_file (char const *from, int volatile *from_needs_removal,
	   char *to, mode_t mode, int backup)
{
  struct stat to_st;
  int to_errno = ! backup ? -1 : stat (to, &to_st) == 0 ? 0 : errno;

  if (backup)
    {
      int try_makedirs_errno = 0;
      char *bakname;

      if (origprae || origbase)
	{
	  char const *p = origprae ? origprae : "";
	  char const *b = origbase ? origbase : "";
	  char const *o = base_name (to);
	  size_t plen = strlen (p);
	  size_t tlen = o - to;
	  size_t blen = strlen (b);
	  size_t osize = strlen (o) + 1;
	  bakname = xmalloc (plen + tlen + blen + osize);
	  memcpy (bakname, p, plen);
	  memcpy (bakname + plen, to, tlen);
	  memcpy (bakname + plen + tlen, b, blen);
	  memcpy (bakname + plen + tlen + blen, o, osize);
	  for (p += FILESYSTEM_PREFIX_LEN (p);  *p;  p++)
	    if (ISSLASH (*p))
	      {
		try_makedirs_errno = ENOENT;
		break;
	      }
	}
      else
	{
	  bakname = find_backup_file_name (to, backup_type);
	  if (!bakname)
	    memory_fatal ();
	}

      if (to_errno)
	{
	  int fd;

	  if (debug & 4)
	    say ("Creating empty unreadable file %s\n", quotearg (bakname));

	  try_makedirs_errno = ENOENT;
	  unlink (bakname);
	  while ((fd = creat (bakname, 0)) < 0)
	    {
	      if (errno != try_makedirs_errno)
		pfatal ("Can't create file %s", quotearg (bakname));
	      makedirs (bakname);
	      try_makedirs_errno = 0;
	    }
	  if (close (fd) != 0)
	    pfatal ("Can't close file %s", quotearg (bakname));
	}
      else
	{
	  if (debug & 4)
	    say ("Renaming file %s to %s\n",
		 quotearg_n (0, to), quotearg_n (1, bakname));
	  while (rename (to, bakname) != 0)
	    {
	      if (errno != try_makedirs_errno)
		pfatal ("Can't rename file %s to %s",
			quotearg_n (0, to), quotearg_n (1, bakname));
	      makedirs (bakname);
	      try_makedirs_errno = 0;
	    }
	}

      free (bakname);
    }

  if (from)
    {
      if (debug & 4)
	say ("Renaming file %s to %s\n",
	     quotearg_n (0, from), quotearg_n (1, to));

      if (rename (from, to) == 0)
	*from_needs_removal = 0;
      else
	{
	  int to_dir_known_to_exist = 0;

	  if (errno == ENOENT
	      && (to_errno == -1 || to_errno == ENOENT))
	    {
	      makedirs (to);
	      to_dir_known_to_exist = 1;
	      if (rename (from, to) == 0)
		{
		  *from_needs_removal = 0;
		  return;
		}
	    }

	  if (errno == EXDEV)
	    {
	      if (! backup)
		{
		  if (unlink (to) == 0)
		    to_dir_known_to_exist = 1;
		  else if (errno != ENOENT)
		    pfatal ("Can't remove file %s", quotearg (to));
		}
	      if (! to_dir_known_to_exist)
		makedirs (to);
	      copy_file (from, to, 0, mode);
	      return;
	    }

	  pfatal ("Can't rename file %s to %s",
		  quotearg_n (0, from), quotearg_n (1, to));
	}
    }
  else if (! backup)
    {
      if (debug & 4)
	say ("Removing file %s\n", quotearg (to));
      if (unlink (to) != 0)
	pfatal ("Can't remove file %s", quotearg (to));
    }
}
Esempio n. 30
0
static void
hub_handle_command(char *buf, uint32_t len)
{
    char *hub_my_nick; /* XXX */

    hub_my_nick = main_to_hub_string(my_nick);

    if (len >= 6 && strncmp(buf, "$Lock ", 6) == 0) {
        char *key;

        if (!check_state(buf, (DCUserState) DC_HUB_LOCK))
            goto hub_handle_command_cleanup;

        key = (char*) memmem(buf+6, len-6, " Pk=", 4);
        if (key == NULL) {
            warn(_("Invalid $Lock message: Missing Pk value\n"));
            key = buf+len;
        }
        key = decode_lock(buf+6, key-buf-6, DC_CLIENT_BASE_KEY);
        if (strleftcmp("EXTENDEDPROTOCOL", buf+6) == 0) {
            if (!hub_putf("$Supports TTHSearch NoGetINFO NoHello|")) {
                free(key);
                goto hub_handle_command_cleanup;
            }
        }
        if (!hub_putf("$Key %s|", key)) {
            free(key);
            goto hub_handle_command_cleanup;
        }
        free(key);
        if (!hub_putf("$ValidateNick %s|", hub_my_nick))
            goto hub_handle_command_cleanup;
        hub_state = DC_HUB_HELLO;
    }
    else if (len >= 10 && strncmp(buf, "$Supports ", 10) == 0) {
        char *p0, *p1;

        hub_extensions = 0;
        for (p0 = buf+10; (p1 = strchr(p0, ' ')) != NULL; p0 = p1+1) {
            *p1 = '\0';
            parse_hub_extension(p0);
        }
        if (*p0 != '\0')
            parse_hub_extension(p0);
    }
    else if (strcmp(buf, "$GetPass") == 0) {
        if (my_password == NULL) {
            screen_putf(_("Hub requires password.\n"));
            hub_disconnect();
            goto hub_handle_command_cleanup;
        }
        screen_putf(_("Sending password to hub.\n"));
        if (!hub_putf("$MyPass %s|", my_password))
            goto hub_handle_command_cleanup;
    }
    else if (strcmp(buf, "$BadPass") == 0) {
        warn(_("Password not accepted.\n"));
        hub_disconnect();
    }
    else if (strcmp(buf, "$LogedIn") == 0) {
        screen_putf(_("You have received operator status.\n"));
    }
    else if (len >= 9 && strncmp(buf, "$HubName ", 9) == 0) {
        free(hub_name);
        hub_name = hub_to_main_string(buf + 9);
        screen_putf(_("Hub name is %s.\n"), quotearg(hub_name));
    }
    else if (strcmp(buf, "$GetNetInfo") == 0) {
        hub_putf("$NetInfo %d$1$%c|", my_ul_slots, is_active ? 'A' : 'P');
    }
    else if (strcmp(buf, "$ValidateDenide") == 0) {
        if (!check_state(buf, (DCUserState) DC_HUB_HELLO))
            goto hub_handle_command_cleanup;
        /* DC++ disconnects immediately if this is received.
         * But shouldn't we give the client a chance to change the nick?
         * Also what happens if we receive this when correctly logged in?
         */
        warn(_("Hub did not accept nick. Nick may be in use.\n"));
        hub_disconnect();
    }
    else if (len >= 7 && strncmp(buf, "$Hello ", 7) == 0) {
        DCUserInfo *ui;
        char *conv_nick;

        conv_nick = hub_to_main_string(buf + 7);

        if (hub_state == DC_HUB_HELLO) {
            if (strcmp(buf+7, hub_my_nick) == 0) {
                screen_putf(_("Nick accepted. You are now logged in.\n"));
            } else {
                /* This probably won't happen, but better safe... */
                free(my_nick);
                my_nick = xstrdup(conv_nick);
                free(hub_my_nick);
                hub_my_nick = xstrdup(buf + 7);
                screen_putf(_("Nick accepted but modified to %s. You are now logged in.\n"), quotearg(my_nick));
            }

            ui = user_info_new(conv_nick);
            ui->info_quered = true; /* Hub is sending this automaticly */
            hmap_put(hub_users, ui->nick, ui);

            free (conv_nick);

            if (!hub_putf("$Version 1,0091|"))
                goto hub_handle_command_cleanup;
            if (!hub_putf("$GetNickList|"))
                goto hub_handle_command_cleanup;
            if (!send_my_info())
                goto hub_handle_command_cleanup;

            hub_state = DC_HUB_LOGGED_IN;
        } else {
            flag_putf(DC_DF_JOIN_PART, _("User %s logged in.\n"), quotearg(conv_nick));
            ui = user_info_new(conv_nick);
            hmap_put(hub_users, ui->nick, ui);
            free (conv_nick);
            if ((hub_extensions & HUB_EXT_NOGETINFO) == 0) {
                if (!hub_putf("$GetINFO %s %s|", buf+7, hub_my_nick))
                    goto hub_handle_command_cleanup;
                ui->info_quered = true;
            }
        }
    }
    else if (len >= 8 && strncmp(buf, "$MyINFO ", 8) == 0) {
        DCUserInfo *ui;
        char *token;
        uint32_t len;
        char* conv_buf;
        char *work_buf;

        buf += 8;
        work_buf = conv_buf = hub_to_main_string(buf);
        token = strsep(&work_buf, " ");
        if (strcmp(token, "$ALL") != 0) {
            warn(_("Invalid $MyINFO message: Missing $ALL parameter, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }

        token = strsep(&work_buf, " ");
        if (token == NULL) {
            warn(_("Invalid $MyINFO message: Missing nick parameter, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }
        ui = (DCUserInfo*) hmap_get(hub_users, token);
        if (ui == NULL) {
            /*
             * if the full buf has not been converted from hub to local charset,
             * we should try to convert nick only
             */
            /*
            char *conv_nick = hub_to_main_string(token);
            if ((ui = hmap_get(hub_users, conv_nick)) == NULL) {
            */
            ui = user_info_new(token);
            ui->info_quered = true;
            hmap_put(hub_users, ui->nick, ui);
            /*
            }
            free(conv_nick);
            */
        }

        token = strsep(&work_buf, "$");
        if (token == NULL) {
            warn(_("Invalid $MyINFO message: Missing description parameter, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }
        free(ui->description);
        ui->description = xstrdup(token);

        token = strsep(&work_buf, "$");
        if (token == NULL) {
            warn(_("Invalid $MyINFO message: Missing description separator, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }

        token = strsep(&work_buf, "$");
        if (token == NULL) {
            warn(_("Invalid $MyINFO message: Missing connection speed, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }
        len = strlen(token);
        free(ui->speed);
        if (len == 0) {
            ui->speed = xstrdup("");
            ui->level = 0; /* XXX: or 1? acceptable level? */
        } else {
            ui->speed = xstrndup(token, len-1);
            ui->level = token[len-1];
        }

        token = strsep(&work_buf, "$");
        if (token == NULL) {
            warn(_("Invalid $MyINFO message: Missing e-mail address, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }
        free(ui->email);
        ui->email = xstrdup(token);

        token = strsep(&work_buf, "$");
        if (token == NULL) {
            warn(_("Invalid $MyINFO message: Missing share size, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }
        if (!parse_uint64(token, &ui->share_size)) {
            warn(_("Invalid $MyINFO message: Invalid share size, ignoring\n"));
            free(conv_buf);
            goto hub_handle_command_cleanup;
        }

        if (ui->active_state == DC_ACTIVE_RECEIVED_PASSIVE
                || ui->active_state == DC_ACTIVE_KNOWN_ACTIVE)
            ui->active_state = DC_ACTIVE_UNKNOWN;

        /* XXX: Now that user's active_state may have changed, try queue again? */
        free(conv_buf);
    }
    else if (strcmp(buf, "$HubIsFull") == 0) {
        warn(_("Hub is full.\n"));
        /* DC++ does not disconnect immediately. So I guess we won't either. */
        /* Maybe we should be expecting an "hub follow" message? */
        /* hub_disconnect(); */
    }
    else if (len >= 3 && (buf[0] == '<' || strncmp(buf, " * ", 3) == 0)) {
        char *head;
        char *tail;
        char *msg;
        bool first = true;
        /*
        int scrwidth;
        size_t firstlen;
        size_t otherlen;

        screen_get_size(NULL, &scrwidth);
        firstlen = scrwidth - strlen(_("Public:"));
        otherlen = scrwidth - strlen(_(" | "));
        */

        msg = prepare_chat_string_for_display(buf);

        for (head = msg; (tail = strchr(head, '\n')) != NULL; head = tail+1) {
            /*PtrV *wrapped;*/

            if (tail[-1] == '\r') /* end > buf here, buf[0] == '<' or ' ' */
                tail[-1] = '\0';
            else
                tail[0] = '\0';

            /*wrapped = wordwrap(quotearg(buf), first ? firstlen : otherlen, otherlen);
            for (c = 0; c < wrapped->cur; c++)
            flag_putf(DC_DF_PUBLIC_CHAT, first ? _("Public: %s\n") : _(" | %s\n"), );
            ptrv_foreach(wrapped, free);
            ptrv_free(wrapped);*/
            flag_putf(DC_DF_PUBLIC_CHAT, first ? _("Public: %s\n") : _(" | %s\n"), quotearg(head));
            first = false;
        }
        flag_putf(DC_DF_PUBLIC_CHAT, first ? _("Public: %s\n") : _(" | %s\n"), quotearg(head));
        free(msg);
    }
    else if (len >= 5 && strncmp(buf, "$To: ", 5) == 0) {
        char *msg;
        char *tail;
        char *frm;
        char *head;
        bool first = true;

        msg = strchr(buf+5, '$');
        if (msg == NULL) {
            warn(_("Invalid $To message: Missing text separator, ignoring\n"));
            goto hub_handle_command_cleanup;
        }
        *msg = '\0';
        msg++;

        /* FIXME: WTF is this? Remove multiple "From: "? Why!? */
        frm = buf+5;
        while ((tail = strstr(msg, "From: ")) != NULL && tail < msg)
            frm = tail+6;

        msg = prepare_chat_string_for_display(msg);
        frm = prepare_chat_string_for_display(frm);
        for (head = msg; (tail = strchr(head, '\n')) != NULL; head = tail+1) {
            if (tail[-1] == '\r') /* tail > buf here because head[0] == '<' or ' ' */
                tail[-1] = '\0';
            else
                tail[0] = '\0';
            if (first) {
                screen_putf(_("Private: [%s] %s\n"), quotearg_n(0, frm), quotearg_n(1, head));
                first = false;
            } else {
                screen_putf(_(" | %s\n"), quotearg(head));
            }
        }
        if (first) {
            screen_putf(_("Private: [%s] %s\n"), quotearg_n(0, frm), quotearg_n(1, head));
        } else {
            screen_putf(_(" | %s\n"), quotearg(head));
        }
        free(msg);
        free(frm);
    }
    else if (len >= 13 && strncmp(buf, "$ConnectToMe ", 13) == 0) {
        struct sockaddr_in addr;

        buf += 13;
        if (strsep(&buf, " ") == NULL) {
            warn(_("Invalid $ConnectToMe message: Missing or invalid nick\n"));
            goto hub_handle_command_cleanup;
        }
        if (!parse_ip_and_port(buf, &addr, 0)) {
            warn(_("Invalid $ConnectToMe message: Invalid address specification.\n"));
            goto hub_handle_command_cleanup;
        }

        flag_putf(DC_DF_CONNECTIONS, _("Connecting to user on %s\n"), sockaddr_in_str(&addr));
        user_connection_new(&addr, -1);
    }
    else if (len >= 16 && strncmp(buf, "$RevConnectToMe ", 16) == 0) {
        char *nick;
        char *local_nick;
        DCUserInfo *ui;

        nick = strtok(buf+16, " ");
        if (nick == NULL) {
            warn(_("Invalid $RevConnectToMe message: Missing nick parameter\n"));
            goto hub_handle_command_cleanup;
        }
        if (strcmp(nick, hub_my_nick) == 0) {
            warn(_("Invalid $RevConnectToMe message: Remote nick is our nick\n"));
            goto hub_handle_command_cleanup;
        }
        local_nick = hub_to_main_string(nick);
        ui = (DCUserInfo*) hmap_get(hub_users, local_nick);
        if (ui == NULL) {
            warn(_("Invalid $RevConnectToMe message: Unknown user %s, ignoring\n"), quotearg(local_nick));
            free(local_nick);
            goto hub_handle_command_cleanup;
        }
        free(local_nick);

        if (ui->conn_count >= DC_USER_MAX_CONN) {
            warn(_("No more connections to user %s allowed.\n"), quotearg(ui->nick));
            goto hub_handle_command_cleanup;
        }

        if (!is_active) {
            if (ui->active_state == DC_ACTIVE_SENT_PASSIVE) {
                warn(_("User %s is also passive. Cannot establish connection.\n"), quotearg(ui->nick));
                /* We could set this to DC_ACTIVE_UNKNOWN too. This would mean
                 * we would keep sending RevConnectToMe next time the download
                 * queue is modified or some timer expired and told us to retry
                 * download. The remote would then send back RevConnectToMe
                 * and this would happen again. This way we would try again -
                 * maybe remote has become active since last time we checked?
                 * But no - DC++ only replies with RevConnectToMe to our first
                 * RevConnectToMe. After that it ignores them all.
                 */
                ui->active_state = DC_ACTIVE_RECEIVED_PASSIVE;
                if (hmap_remove(pending_userinfo, ui->nick) != NULL)
                    ui->refcount--;
                goto hub_handle_command_cleanup;
            }
            if (ui->active_state != DC_ACTIVE_RECEIVED_PASSIVE) {
                /* Inform remote that we are also passive. */
                if (!hub_putf("$RevConnectToMe %s %s|", hub_my_nick, nick))
                    goto hub_handle_command_cleanup;
            }
        }
        ui->active_state = DC_ACTIVE_RECEIVED_PASSIVE;

        if (!hub_connect_user(ui))
            goto hub_handle_command_cleanup;
    }
    else if ( (len >= 10 && strncmp(buf, "$NickList ", 10) == 0)
              || (len >= 8 && strncmp(buf, "$OpList ", 8) == 0) ) {
        char *nick;
        char *end;
        int oplist;
        char *conv_nick;

        if ( strncmp(buf, "$NickList ", 10) == 0) {
            nick = buf + 10;
            oplist = 0;
        } else {
            nick = buf + 8;
            oplist = 1;
        }

        for (; (end = strstr(nick, "$$")) != NULL; nick = end+2) {
            DCUserInfo *ui;
            *end = '\0';

            conv_nick = hub_to_main_string(nick);

            ui = (DCUserInfo*) hmap_get(hub_users, conv_nick);
            if (ui == NULL) {
                ui = user_info_new(conv_nick);
                hmap_put(hub_users, ui->nick, ui);
            }

            free(conv_nick);

            if (!ui->info_quered && (hub_extensions & HUB_EXT_NOGETINFO) == 0) {
                if (!hub_putf("$GetINFO %s %s|", nick, hub_my_nick))  {
                    goto hub_handle_command_cleanup;
                }
                ui->info_quered = true;
            }

            if (oplist)
                ui->is_operator = true;
        }
    }
    else if (len >= 6 && strncmp(buf, "$Quit ", 6) == 0) {
        DCUserInfo *ui;
        char *conv_nick = hub_to_main_string(buf+6);

        // I don't want these messages.
        //flag_putf(DC_DF_JOIN_PART, "User %s quits.\n", quotearg(conv_nick));
        ui = (DCUserInfo*) hmap_remove(hub_users, conv_nick);
        if (ui == NULL) {
            /* Some hubs print quit messages for users that never joined,
             * so print this as debug only.
             */
            flag_putf(DC_DF_DEBUG, _("Invalid $Quit message: Unknown user %s.\n"), quotearg(conv_nick));
        } else
            user_info_free(ui);
        free(conv_nick);
    }
    else if (len >= 8 && strncmp(buf, "$Search ", 8) == 0) {
        char *source;
        int parse_result = 0;
        DCSearchSelection sel;
        sel.patterns = NULL;

        buf += 8;
        source = strsep(&buf, " ");
        if (source == NULL) {
            warn(_("Invalid $Search message: Missing source specification.\n"));
            goto hub_handle_command_cleanup;
        }
        /* charset handling is in parse_search_selection */
        parse_result = parse_search_selection(buf, &sel);
        if (parse_result != 1) {
            if (sel.patterns != NULL) {
                int i = 0;
                for (i = 0; i < sel.patterncount; i++) {
                    search_string_free(sel.patterns+i);
                }
                free(sel.patterns);
            }
            if (parse_result == 0)
                warn(_("Invalid $Search message: %s: Invalid search specification.\n"), buf);
            goto hub_handle_command_cleanup;
        }
        if (strncmp(source, "Hub:", 4) == 0) {
            DCUserInfo *ui;
            char *conv_nick = hub_to_main_string(source+4);
            ui = (DCUserInfo*) hmap_get(hub_users, conv_nick);

            if (ui == NULL) {
                warn(_("Invalid $Search message: Unknown user %s.\n"), quotearg(conv_nick));
                if (sel.patterns != NULL) {
                    int i = 0;
                    for (i = 0; i < sel.patterncount; i++) {
                        search_string_free(sel.patterns+i);
                    }
                    free(sel.patterns);
                }
                free(conv_nick);
                goto hub_handle_command_cleanup;
            }
            free(conv_nick);

            if (strcmp(ui->nick, my_nick) == 0) {
                if (sel.patterns != NULL) {
                    int i = 0;
                    for (i = 0; i < sel.patterncount; i++) {
                        search_string_free(sel.patterns+i);
                    }
                    free(sel.patterns);
                }
                goto hub_handle_command_cleanup;
            }
            perform_inbound_search(&sel, ui, NULL);
            if (sel.patterns != NULL) {
                int i = 0;
                for (i = 0; i < sel.patterncount; i++) {
                    search_string_free(sel.patterns+i);
                }
                free(sel.patterns);
            }
        } else {
            struct sockaddr_in addr;
            if (!parse_ip_and_port(source, &addr, DC_CLIENT_UDP_PORT)) {
                warn(_("Invalid $Search message: Invalid address specification.\n"));
                if (sel.patterns != NULL) {
                    int i = 0;
                    for (i = 0; i < sel.patterncount; i++) {
                        search_string_free(sel.patterns+i);
                    }
                    free(sel.patterns);
                }
                goto hub_handle_command_cleanup;
            }
            if (local_addr.sin_addr.s_addr == addr.sin_addr.s_addr && listen_port == ntohs(addr.sin_port)) {
                if (sel.patterns != NULL) {
                    int i = 0;
                    for (i = 0; i < sel.patterncount; i++) {
                        search_string_free(sel.patterns+i);
                    }
                    free(sel.patterns);
                }
                goto hub_handle_command_cleanup;
            }
            perform_inbound_search(&sel, NULL, &addr);
            if (sel.patterns != NULL) {
                int i = 0;
                for (i = 0; i < sel.patterncount; i++) {
                    search_string_free(sel.patterns+i);
                }
                free(sel.patterns);
            }
        }
    } else if (len >= 4 && strncmp(buf, "$SR ", 4) == 0) {
        handle_search_result(buf, len);
    } else if (len == 0) {
        /* Ignore empty commands. */
    }
hub_handle_command_cleanup:
    free(hub_my_nick);
}