Add the friend cache.
[cascardo/f2fchat.git] / friend.c
1 /*
2  *  Copyright (C) 2013  Thadeu Lima de Souza Cascardo <cascardo@cascardo.info>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include "friend.h"
20 #include <stdio.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28
29 static int connect_friend(struct sockaddr *saddr, char *address, char *port, int *c)
30 {
31         struct addrinfo *addresses;
32         struct addrinfo *addr;
33         struct addrinfo hint;
34         int r;
35         int fd = *c = -1;
36         int i;
37         memset(&hint, 0, sizeof(hint));
38         hint.ai_family = AF_UNSPEC;
39         hint.ai_socktype = SOCK_STREAM;
40         hint.ai_protocol = IPPROTO_TCP;
41         hint.ai_flags = AI_ADDRCONFIG;
42         r = getaddrinfo(address, port, &hint, &addresses);
43         if (r) {
44                 return r;
45         }
46         for (addr = addresses; addr != NULL; addr = addr->ai_next) {
47                 fd = socket(addr->ai_family, addr->ai_socktype,
48                                 addr->ai_protocol);
49                 if (fd >= 0)
50                         break;
51                 close(fd);
52                 fd = -1;
53         }
54         freeaddrinfo(addresses);
55         *c = fd;
56         if (fd == -1)
57                 return EAI_SYSTEM;
58         return 0;
59 }
60
61 struct friend {
62         char *name;
63         char *address;
64         char *port;
65         struct sockaddr *saddr;
66 };
67
68 struct cache {
69         int nfriends;
70         struct friend *friends;
71 };
72
73 int create_cache(struct cache **cache)
74 {
75         *cache = malloc(sizeof(*cache));
76         if (!*cache)
77                 return -errno;
78         (*cache)->nfriends = 0;
79         (*cache)->friends = NULL;
80         return 0;
81 }
82
83 int destroy_cache(struct cache *cache)
84 {
85         if (cache->friends)
86                 free(cache->friends);
87         free(cache);
88 }
89
90 int cache_add_friend(struct cache *cache, char *friend, char *address, char *port)
91 {
92         struct friend *tfriend;
93         if (!cache->friends) {
94                 cache->friends = malloc(sizeof(struct friend));
95                 cache->nfriends = 1;
96         } else {
97                 struct friend *new_friends;
98                 cache->nfriends++;
99                 new_friends = realloc(cache->friends, cache->nfriends * sizeof(struct friend));
100                 if (!new_friends) {
101                         cache->nfriends--;
102                         return -errno;
103                 }
104                 cache->friends = new_friends;
105         }
106         tfriend = &cache->friends[cache->nfriends - 1];
107         tfriend->name = friend;
108         tfriend->address = address;
109         tfriend->port = port;
110         tfriend->saddr = NULL;
111         return 0;
112 }
113
114 int load_cache(struct cache *cache, char *fname)
115 {
116         FILE *file;
117         int err = 0;
118         char *buffer = NULL;
119         size_t len = 0;
120         int r;
121         file = fopen(fname, "r");
122         if (!file)
123                 return -errno;
124         while ((r = getline(&buffer, &len, file)) > 0) {
125                 char *name;
126                 char *address;
127                 char *port;
128                 char *end;
129                 name = buffer;
130                 address = name;
131                 while (*++address != '\t');
132                 *address++ = '\0';
133                 port = address;
134                 while (*++port != '\t');
135                 *port++ = '\0';
136                 end = port;
137                 while (*++end != '\n');
138                 *end = '\0';
139                 fprintf(file, "%s\t%s\t%s\n", name, address, port);
140                 cache_add_friend(cache, strdup(name), strdup(address), strdup(port));
141         }
142 out:
143         fclose(file);
144         return err;
145 }
146
147 int store_cache(struct cache *cache, char *fname)
148 {
149         FILE *file;
150         int err = 0;
151         int i;
152         file = fopen(fname, "w");
153         if (!file)
154                 return -errno;
155         for (i = 0; i < cache->nfriends; i++) {
156                 struct friend *friend = &cache->friends[i];
157                 fprintf(file, "%s\t%s\t%s\n", friend->name, friend->address, friend->port);
158         }
159 out:
160         fclose(file);
161         return err;
162 }