aboutsummaryrefslogtreecommitdiffstats
path: root/libcontacts_save_contact.c
blob: 2e64c4ad387f0e9eb1750a4af4249b37368b663d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/* See LICENSE file for copyright and license details. */
#include "common.h"


int
libcontacts_save_contact(struct libcontacts_contact *contact, const struct passwd *user)
{
	char *data = NULL, *path = NULL, *tmppath;
	int oflags = O_WRONLY | O_CREAT | O_TRUNC;
	int fd = -1, saved_errno = errno;
	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;
			} else {
				contact->id = strdup(contact->name);
				if (!contact->id)
					goto fail;
				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 = alloca(strlen(path) + sizeof("~"));
	stpcpy(stpcpy(tmppath, path), "~");

	fd = open(tmppath, oflags, 0666);
	if (fd < 0) {
		if ((oflags & O_EXCL) && errno == EEXIST) {
			if (!num++) {
				basenam = contact->id;
				contact->id = NULL;
			}
			goto generate_id;
		}
		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(basenam);
	errno = saved_errno;
	return 0;

fail:
	saved_errno = errno;
	if (fd >= 0)
		close(fd);
	free(data);
	free(path);
	free(basenam);
	errno = saved_errno;
	return -1;
}