/* See LICENSE file for copyright and license details. */ #include "common.h" static int makedirs(char *path) { char *p, *last; last = strrchr(path, '/'); if (!last) return 0; *last = '\0'; for (p = path; *p == '/'; p++); while (*p) { for (; *p && *p != '/'; p++); *p = '\0'; if (mkdir(path, 0777) && errno != EEXIST) { *last = *p = '/'; return -1; } for (*p++ = '/'; *p == '/'; p++); } *last = '/'; return 0; } int libcontacts_save_contact(struct libcontacts_contact *contact, const struct passwd *user) { char *data = NULL, *path = NULL, *tmppath = NULL; int oflags = O_WRONLY | O_CREAT | O_TRUNC, newid = 0; int fd = -1, saved_errno = errno, dirs_created = 0; ssize_t r; size_t p, n, num = 0; char *basenam = NULL; if (libcontacts_format_contact(contact, &data)) goto fail; if (!contact->id) { oflags = O_WRONLY | O_CREAT | O_EXCL; generate_id: if (!num) { if (!contact->name || !*contact->name) { contact->id = strdup("unnamed"); if (!contact->id) goto fail; newid = 1; } else { contact->id = strdup(contact->name); if (!contact->id) goto fail; newid = 1; for (p = 0; contact->id[p]; p++) { if (isalpha(contact->id[p])) contact->id[p] = (char)tolower(contact->id[p]); else contact->id[p] = '-'; } } } else { free(contact->id); snprintf(NULL, 0, "%s-%zu%zn", basenam, num, &r); contact->id = malloc((size_t)r + 1); if (!contact->id) goto fail; sprintf(contact->id, "%s-%zu", basenam, num); } } path = libcontacts_get_path(contact->id, user); if (!path) goto fail; tmppath = malloc(strlen(path) + sizeof("~")); stpcpy(stpcpy(tmppath, path), "~"); if (oflags & O_EXCL) { open_excl_again: fd = open(path, oflags, 0666); if (fd < 0) { if (errno == ENOENT && !dirs_created) { if (makedirs(path)) goto fail; dirs_created = 1; goto open_excl_again; } if (errno != EEXIST) goto fail; if (!num++) { basenam = contact->id; contact->id = NULL; } free(path); free(tmppath); goto generate_id; } close(fd); oflags ^= O_EXCL ^ O_TRUNC; dirs_created = 1; } open_again: fd = open(tmppath, oflags, 0666); if (fd < 0) { if (errno == ENOENT && !dirs_created) { if (makedirs(path)) goto fail; dirs_created = 1; goto open_again; } goto fail; } n = strlen(data); for (p = 0; p < n; p += (size_t)r) { r = write(fd, &data[p], n - p); if (r < 0) goto fail; } if (fsync(fd)) goto fail; if (close(fd)) { fd = -1; goto fail; } fd = -1; if (rename(tmppath, path)) goto fail; free(data); free(path); free(tmppath); free(basenam); errno = saved_errno; return 0; fail: saved_errno = errno; if (fd >= 0) close(fd); free(data); free(path); free(tmppath); free(basenam); if (newid) { free(contact->id); contact->id = NULL; } errno = saved_errno; return -1; }