a7f2e5c7d7ff7ae1fac8830566c516e2becc779b
[banana.git] / banana / nntp.inc.php
1 <?php
2 /********************************************************************************
3 * banana/nntp.inc.php : NNTP protocole handler
4 * ------------------------
5 *
6 * This file is part of the banana distribution
7 * Copyright: See COPYING files that comes with this distribution
8 ********************************************************************************/
9
10 require_once dirname(__FILE__) . '/banana.inc.php';
11 require_once dirname(__FILE__) . '/message.inc.php';
12 require_once dirname(__FILE__) . '/nntpcore.inc.php';
13 require_once dirname(__FILE__) . '/protocoleinterface.inc.php';
14
15 class BananaNNTP extends BananaNNTPCore implements BananaProtocoleInterface
16 {
17 private $ingroup = null;
18
19 private $mode = null;
20 private $boxes = null;
21
22 /** Build a protocole handler plugged on the given box
23 */
24 public function __construct()
25 {
26 $url = parse_url(Banana::$nntp_host);
27 if ($url['scheme'] == 'nntps' || $url['scheme'] == 'snntp') {
28 $url['host'] = 'ssl://' . $url['host'];
29 }
30 if (!isset($url['port'])) {
31 $url['port'] = 119;
32 }
33 parent::__construct($url['host'], $url['port']);
34 if (isset($url['user'])) {
35 $this->authinfo($url['user'], $url['pass']);
36 }
37 }
38
39 /** Return the descript;ion of the current box
40 */
41 public function getDescription()
42 {
43 $descs = $this->xgtitle(Banana::$group);
44 if (isset($descs[Banana::$group])) {
45 return trim(utf8_encode($descs[Banana::$group]));
46 }
47 return null;
48 }
49
50 /** Return the list of the boxes
51 * @param mode Kind of boxes to list
52 * @param since date of last check (for new boxes and new messages)
53 * @return Array(boxname => array(desc => boxdescripton, msgnum => number of message, unread =>number of unread messages)
54 */
55 public function getBoxList($mode = Banana::BOXES_ALL, $since = 0, $withstats = false)
56 {
57 if (!is_array($this->boxes) || $this->mode != $mode) {
58 $descs = $this->xgtitle();
59 if ($mode == Banana::BOXES_NEW && $since) {
60 $list = $this->newgroups($since);
61 } else {
62 $list = $this->listGroups();
63 if ($mode == Banana::BOXES_SUB) {
64 $sub = array_flip(Banana::$profile['subscribe']);
65 $list = array_intersect_key($list, $sub);
66 }
67 }
68 $this->boxes = array();
69 foreach ($list as $group=>&$infos) {
70 if (isset($descs[$group])) {
71 $desc = $descs[$group];
72 if (!is_utf8($desc)) {
73 $desc = utf8_encode($desc);
74 }
75 $this->boxes[$group] = array('desc' => $desc);
76 } else {
77 $this->boxes[$group] = array('desc' => null);
78 }
79 }
80 ksort($this->boxes);
81 }
82 if ($withstats) {
83 foreach ($this->boxes as $group=>&$desc) {
84 list($msgnum, $first, $last, $groupname) = $this->group($group);
85 $this->ingroup = $group;
86 $new = count($this->newnews($group, $since));
87 $desc['msgnum'] = $msgnum;
88 $desc['unread'] = $new;
89 }
90 }
91 return $this->boxes;
92 }
93
94 /** Return a message
95 * @param id Id of the emssage (can be either an Message-id or a message index)
96 * @return A BananaMessage or null if the given id can't be retreived
97 */
98 public function &getMessage($id)
99 {
100 $message = null;
101 if (is_numeric($id) && Banana::$group != $this->ingroup) {
102 if (is_null(Banana::$spool)) {
103 $this->group(Banana::$group);
104 $this->ingroup = Banana::$group;
105 } else {
106 $id = array_search($id, Banana::$spool->ids);
107 }
108 }
109 $data = $this->article($id);
110 if ($data !== false) {
111 $message = new BananaMessage($data);
112 }
113 return $message;
114 }
115
116 /** Return the sources of the message
117 */
118 public function getMessageSource($id)
119 {
120 if (is_numeric($id) && Banana::$group != $this->ingroup) {
121 if (is_null(Banana::$spool)) {
122 $this->group(Banana::$group);
123 $this->ingroup = Banana::$group;
124 } else {
125 $id = array_search($id, Banana::$spool->ids);
126 }
127 }
128 $data = $this->article($id);
129 if ($data !== false) {
130 return implode("\n", $data);
131 }
132 $data = null;
133 return $data;
134 }
135
136 /** Return the indexes of the messages presents in the Box
137 * @return Array(number of messages, MSGNUM of the first message, MSGNUM of the last message)
138 */
139 public function getIndexes()
140 {
141 list($msgnum, $first, $last, $groupname) = $this->group(Banana::$group);
142 $this->ingroup = Banana::$group;
143 return array($msgnum, $first, $last);
144 }
145
146 /** Return the message headers (in BananaMessage) for messages from firstid to lastid
147 * @return Array(id => array(headername => headervalue))
148 */
149 public function &getMessageHeaders($firstid, $lastid, array $msg_headers = array())
150 {
151 $messages = array();
152 foreach ($msg_headers as $header) {
153 $headers = $this->xhdr($header, $firstid, $lastid);
154 $header = strtolower($header);
155 if ($header == 'date') {
156 $headers = array_map('strtotime', $headers);
157 } else {
158 array_walk($headers, array('BananaMimePart', 'decodeHeader'));
159 }
160 foreach ($headers as $id=>&$value) {
161 if (!isset($messages[$id])) {
162 $messages[$id] = array();
163 }
164 $messages[$id][$header] =& $value;
165 }
166 }
167 return $messages;
168 }
169
170 /** Add protocole specific data in the spool
171 */
172 public function updateSpool(array &$messages)
173 {
174 return true;
175 }
176
177 /** Return the indexes of the new messages since the give date
178 * @return Array(MSGNUM of new messages)
179 */
180 public function getNewIndexes($since)
181 {
182 return $this->newnews(Banana::$group, $since);
183 }
184
185 /** Return true if can post
186 */
187 public function canSend()
188 {
189 return $this->isValid();
190 }
191
192 /** Return true if can cancel
193 */
194 public function canCancel()
195 {
196 return $this->isValid();
197 }
198
199 /** Return the list of requested header for a new post
200 */
201 public function requestedHeaders()
202 {
203 return Array('From', 'Subject', 'dest' => 'Newsgroups', 'reply' => 'Followup-To', 'Organization');
204 }
205
206 /** Send the message
207 */
208 public function send(BananaMessage &$message)
209 {
210 $sources = $message->get(true);
211 return $this->post($sources);
212 }
213
214 /** Cancel the message
215 */
216 public function cancel(BananaMessage &$message)
217 {
218 $headers = Array('From' => Banana::$profile['From'],
219 'Newsgroups' => Banana::$group,
220 'Subject' => 'cmsg ' . $message->getHeaderValue('message-id'),
221 'Control' => 'cancel ' . $message->getHeaderValue('message-id'));
222 $headers = array_merge($headers, Banana::$msgedit_headers);
223 $body = 'Message canceled with Banana';
224 $msg = BananaMessage::newMessage($headers, $body);
225 return $this->send($msg);
226 }
227
228 /** Return the protocole name
229 */
230 public function name()
231 {
232 return 'NNTP';
233 }
234
235 /** Return the filename for the spool
236 */
237 public function filename()
238 {
239 $url = parse_url(Banana::$nntp_host);
240 $file = '';
241 if (isset($url['host'])) {
242 $file .= $url['host'] . '_';
243 }
244 if (isset($url['port'])) {
245 $file .= $url['port'] . '_';
246 }
247 $file .= Banana::$group;
248 return $file;
249 }
250 }
251
252 // vim:set et sw=4 sts=4 ts=4 enc=utf-8:
253 ?>