-int is_safe_path(const char *file)
-{
- char *p;
- const char *f;
- char x;
- struct stat s;
- char l[MAXBUFSIZE];
-
- if(*file != '/')
- {
- syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
- return 0;
- }
-
- p = strrchr(file, '/');
-
- if(p == file) /* It's in the root */
- p++;
-
- x = *p;
- *p = '\0';
-
- f = file;
-check1:
- if(lstat(f, &s) < 0)
- {
- syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
- return 0;
- }
-
- if(s.st_uid != geteuid())
- {
- syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
- f, s.st_uid, geteuid());
- return 0;
- }
-
- if(S_ISLNK(s.st_mode))
- {
- syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
- f);
-
- if(readlink(f, l, MAXBUFSIZE) < 0)
- {
- syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
- return 0;
- }
-
- f = l;
- goto check1;
- }
-
- *p = x;
- f = file;
-
-check2:
- if(lstat(f, &s) < 0 && errno != ENOENT)
- {
- syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
- return 0;
- }
-
- if(errno == ENOENT)
- return 1;
-
- if(s.st_uid != geteuid())
- {
- syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
- f, s.st_uid, geteuid());
- return 0;
- }
-
- if(S_ISLNK(s.st_mode))
- {
- syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
- f);
-
- if(readlink(f, l, MAXBUFSIZE) < 0)
- {
- syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
- return 0;
- }
-
- f = l;
- goto check2;
- }
-
- if(s.st_mode & 0007)
- {
- /* Accessible by others */
- syslog(LOG_ERR, _("`%s' has unsecure permissions"),
- f);
- return 0;
- }
-
- return 1;
+bool change_config_file(meshlink_handle_t *mesh, const char *name, const char *key, const char *value) {
+ char filename[PATH_MAX];
+ char tmpname[PATH_MAX];
+ char buf[MAX_STRING_SIZE];
+ const int keylen = strlen(key);
+
+ snprintf(filename, PATH_MAX, "%s" SLASH "hosts" SLASH "%s", mesh->confbase, name);
+ snprintf(tmpname, PATH_MAX, "%s.tmp", filename);
+
+ FILE *in = fopen(filename, "r");
+ if(!in) {
+ // Hm, maybe the file does not exist? Try appending.
+ return append_config_file(mesh, name, key, value);
+ }
+
+ FILE *out = fopen(tmpname, "w");
+ if(!out) {
+ logger(mesh, MESHLINK_ERROR, "Failed to write `%s': %s", tmpname, strerror(errno));
+ fclose(in);
+ return false;
+ }
+
+ bool ignore = false;
+
+ while(readline(in, buf, sizeof buf)) {
+ if(ignore) {
+ if(!strncmp(buf, "-----END", 8))
+ ignore = false;
+ } else {
+ if(!strncmp(buf, "-----BEGIN", 10))
+ ignore = true;
+ }
+
+ if(!ignore && !strncmp(buf, key, keylen)) {
+ if(strchr("\t =", buf[keylen])) {
+ continue;
+ }
+ }
+
+ fputs(buf, out);
+ fputc('\n', out);
+ }
+
+ if(ferror(in)) {
+ logger(mesh, MESHLINK_ERROR, "Failed to read `%s': %s", filename, strerror(errno));
+ fclose(in);
+ fclose(out);
+ return false;
+ }
+
+ fclose(in);
+
+ fprintf(out, "%s = %s\n", key, value);
+
+ if(ferror(out)) {
+ logger(mesh, MESHLINK_ERROR, "Failed to write `%s': %s", tmpname, strerror(errno));
+ fclose(out);
+ return false;
+ }
+
+ fclose(out);
+
+#ifdef HAVE_MINGW
+ // We cannot atomically replace files on Windows.
+ char bakname[PATH_MAX];
+ snprintf(bakname, PATH_MAX, "%s.bak", filename);
+ if(rename(tmpname, bakfile) || rename(bakfile, filename)) {
+ rename(bakfile, filename);
+#else
+ if(rename(tmpname, filename)) {
+#endif
+ logger(mesh, MESHLINK_ERROR, "Failed to update `%s': %s", filename, strerror(errno));
+ return false;
+ }
+
+ return true;