aboutsummaryrefslogblamecommitdiffstats
path: root/set-contact-photos.c
blob: 744e8a295148a69aa559ab1464a29fbf8fb5bbdb (plain) (tree)
1
2
3
4


                                                         
                                        












                                                               




                                              
                                                      
                 



















                                                                      

                                    

                                            
                                       
                                  
                                                 










                                                                                                 
                                 
                                      
                                                    






















                                                                                             
                                                    









































































                                                                                        




                        
                                         

                        
                        




























                                                                                           

                                                                                           







                                                                      

                                               

                                           
                                                              



                                                                                        


















                                                                                                     
/* See LICENSE file for copyright and license details. */
#include "common.h"

USAGE("[-r | -u] contact-id ... photo");


static char *
get_target(const char *path, size_t *lenp)
{
	char *target = NULL;
	size_t size = 0;
	ssize_t n = 0;

        do {
                if ((size_t)n == size)
                        target = erealloc(target, size += 512);
                n = readlink(path, target, size);
                if (n < 0) {
			if (errno == EINVAL) {
				free(target);
				return NULL;
			}
			eprintf("readlink %s:", path);
		}
        } while ((size_t)n >= size);

	*lenp = (size_t)n;
	target[n] = '\0';

	return target;

}

static char *
get_absolute_path(const char *path, const char *cwd)
{
	char *ret, *r, *w, *target, *new;
	size_t len1, len2, len3, targetlen, retlennul, looplimit = 64;

	if (path[0] == '/') {
		retlennul = strlen(path) + 1;
		ret = emalloc(retlennul);
		memcpy(ret, path, retlennul);
	} else {
		len1 = strlen(cwd);
		len2 = strlen(path);
		retlennul = len1 + len2 + 2;
		ret = emalloc(retlennul);
		memcpy(ret, cwd, len1);
		ret[len1++] = '/';
		memcpy(&ret[len1], path, ++len2);
	}

again:
	for (w = r = ret; *r;) {
		if (r[0] == '/' && (r[1] == '/' || !r[1])) {
			r += 1;
		} else if (r[0] == '/' && r[1] == '.' && (r[2] == '/' || !r[2])) {
			r += 2;
		} else if (r[0] == '/' && r[1] == '.' && r[2] == '.' && (r[3] == '/' || !r[3])) {
			*w = '\0';
			target = get_target(ret, &targetlen);
			*w = '/';
			if (!target) {
				while (*--w != '/');
				r += 3;
			} else if (!target[0]) {
				eprintf("%s: encountered symlink with empty target\n", path);
			} else if (target[0] == '/') {
				if (!looplimit--) {
					errno = ELOOP;
					eprintf("%s:", path);
				}
				len1 = targetlen;
				len2 = retlennul - (size_t)(r - ret);
				retlennul = len1 + len2;
				new = emalloc(retlennul);
				memcpy(new, target, len1);
				memcpy(&new[len1], r, len2);
				free(ret);
				ret = new;
				free(target);
				goto again;
			} else {
				if (!looplimit--) {
					errno = ELOOP;
					eprintf("%s:", path);
				}
				while (*--w != '/');
				len1 = (size_t)(w - ret);
				len2 = targetlen;
				len3 = retlennul - (size_t)(r - ret);
				retlennul = len1 + len2 + len3;
				new = emalloc(retlennul);
				memcpy(new, ret, len1);
				memcpy(&new[len1], target, len2);
				memcpy(&new[len1 + len2], r, len3);
				free(ret);
				ret = new;
				r = w = &ret[len1];
				free(target);
			}
		} else {
			*w++ = *r++;
		}
	}

	*w = '\0';
	if (!*ret) {
		ret[0] = '/';
		ret[1] = '\0';
	}
	return ret;
}

static const char *
get_cwd(char **free_this)
{
        char *cwd = NULL;
        size_t size = 64 / 2;
        int saved_errno = errno;
        const char *pwd;
	struct stat cst, pst;

        *free_this = NULL;
	for (;;) {
                cwd = erealloc(*free_this, size *= 2);
                *free_this = cwd;
		if (getcwd(cwd, size))
                        break;
                if (errno != ERANGE)
			eprintf("getcwd %zu:", size);
        }

        errno = saved_errno;
        if (!(pwd = getenv("PWD")) || *pwd != '/' || stat(pwd, &pst) || stat(cwd, &cst))
                return cwd;

	if (pst.st_dev == cst.st_dev && pst.st_ino == cst.st_ino) {
		free(*free_this);
		*free_this = NULL;
		return pwd;
	}
        return cwd;
}

int
main(int argc, char *argv[])
{
	int remove = 0, as_is = 0;
	struct passwd *user;
	struct libcontacts_contact contact;
	char *photo, **r, **w, *newpath = NULL, *home, *cwd_free;
	const char *cwd;
	int ret = 0;
	size_t i, len;

	ARGBEGIN {
	case 'r':
		as_is = 1;
		break;
	case 'u':
		remove = 1;
		break;
	default:
		usage();
	} ARGEND;

	if (argc < 2 || (as_is & remove))
		usage();

	as_is |= remove;
	photo = argv[--argc];
	argv[argc] = NULL;

	for (i = 0; argv[i]; i++)
		if (!*argv[i] || strchr(argv[i], '/'))
			usage();

	errno = 0;
	user = getpwuid(getuid());
	if (!user)
		eprintf("getpwuid: %s\n", errno ? strerror(errno) : "user does not exist");

	if (!as_is && user->pw_dir && user->pw_dir[0] == '/' && photo[0] != '/') {
		cwd = get_cwd(&cwd_free);
		if (cwd[0] != '/')
			abort();
		newpath = get_absolute_path(photo, cwd);
		home = get_absolute_path(user->pw_dir, cwd);
		free(cwd_free);
		len = strlen(home);
		if (!strncmp(newpath, home, len) && newpath[len] == '/')
			photo = &newpath[len + 1];
		else
			photo = newpath;
		free(home);
	}

	for (; *argv; argv++) {
		if (libcontacts_load_contact(*argv, &contact, user)) {
			weprintf("libcontacts_load_contact %s: %s\n", *argv,
			         errno ? strerror(errno) : "contact file is malformatted");
			ret = 1;
			continue;
		}
		if (contact.photos) {
			for (i = 0; contact.photos[i]; i++)
				if (!strcmp(contact.photos[i], photo))
					break;
			r = &contact.photos[i];
			if (!remove && !*r)
				goto add_photo;
			if (remove && *r) {
				free(*r);
				for (w = r++; (*w++ = *r++););
				if (libcontacts_save_contact(&contact, user)) {
					weprintf("libcontacts_save_contact %s:", *argv);
					ret = 1;
				}
			}
		} else if (!remove) {
			i = 0;
		add_photo:
			contact.photos = erealloc(contact.photos, (i + 2) * sizeof(*contact.photos));
			contact.photos[i + 1] = NULL;
			contact.photos[i] = photo;
			if (libcontacts_save_contact(&contact, user)) {
				weprintf("libcontacts_save_contact %s:", *argv);
				ret = 1;
			}
			contact.photos[i] = NULL;
		}
		libcontacts_contact_destroy(&contact);
	}

	free(newpath);
	return ret;
}