1// Sun, 18 Jun 2000
2// Y.Takagi
3
4#include "IppTransport.h"
5#include "DbgMsg.h"
6
7#include <PrintTransportAddOn.h>
8#include <NetEndpoint.h>
9#include <String.h>
10#include <OS.h>
11
12#include <HashString.h>
13#include <HashMap.h>
14
15IppTransport *transport = NULL;
16
17// Set transport_features so we stay loaded
18uint32 transport_features = B_TRANSPORT_IS_HOTPLUG | B_TRANSPORT_IS_NETWORK;
19
20
21class IPPPrinter {
22public:
23	IPPPrinter(const BString& uri, uint32 type)
24		{ fURI=uri; fType=type; }
25
26	uint32 fType, fState;
27	BString fURI, fLocation, fInfo, fMakeModel, fAttributes;
28};
29
30
31class IPPPrinterRoster {
32public:
33	IPPPrinterRoster();
34	~IPPPrinterRoster();
35
36	status_t ListPorts(BMessage *msg);
37	status_t Listen();
38private:
39	char *_ParseString(BString& outStr, char*& pos);
40
41	static status_t _IPPListeningThread(void *cookie);
42
43	typedef HashMap<HashString,IPPPrinter*> IPPPrinterMap;
44	IPPPrinterMap fPrinters;
45	BNetEndpoint *fEndpoint;
46	thread_id fListenThreadID;
47};
48
49
50static IPPPrinterRoster gRoster;
51
52
53IPPPrinterRoster::IPPPrinterRoster()
54{
55	// Setup our (UDP) listening endpoint
56	fEndpoint = new BNetEndpoint(SOCK_DGRAM);
57	if (!fEndpoint)
58		return;
59
60	if (fEndpoint->InitCheck() != B_OK)
61		return;
62
63	if (fEndpoint->Bind(BNetAddress(INADDR_ANY, 631)) != B_OK)
64		return;
65
66	// Now create thread for listening
67	fListenThreadID = spawn_thread(_IPPListeningThread, "IPPListener", B_LOW_PRIORITY, this);
68	if (fListenThreadID <= 0)
69		return;
70
71	resume_thread(fListenThreadID);
72}
73
74
75IPPPrinterRoster::~IPPPrinterRoster()
76{
77	kill_thread(fListenThreadID);
78	delete fEndpoint;
79}
80
81
82status_t
83IPPPrinterRoster::Listen()
84{
85	BNetAddress srcAddress;
86	uint32 type, state;
87	char packet[1541];
88	char uri[1024];
89	char* pos;
90	int32 len;
91
92	while ((len=fEndpoint->ReceiveFrom(packet, sizeof(packet) -1, srcAddress)) > 0) {
93		packet[len] = '\0';
94
95		// Verify packet format
96		if (sscanf(packet, "%" B_SCNx32 "%" B_SCNx32 "%1023s", &type, &state,
97			uri) == 3) {
98			IPPPrinter *printer = fPrinters.Get(uri);
99			if (!printer) {
100				printer = new IPPPrinter(uri, type);
101				fPrinters.Put(printer->fURI.String(), printer);
102			}
103
104			printer->fState=state;
105
106			// Check for option parameters
107			if ((pos=strchr(packet, '"')) != NULL) {
108				BString str;
109				if (_ParseString(str, pos))
110					printer->fLocation = str;
111				if (pos && _ParseString(str, pos))
112					printer->fInfo = str;
113				if (pos && _ParseString(str, pos))
114					printer->fMakeModel = str;
115
116				if (pos)
117					printer->fAttributes = pos;
118			}
119
120			DBGMSG(("Printer: %s\nLocation: %s\nInfo: %s\nMakeModel: %s\nAttributes: %s\n",
121				printer->fURI.String(), printer->fLocation.String(), printer->fInfo.String(),
122				printer->fMakeModel.String(), printer->fAttributes.String()));
123		}
124	}
125
126	return len;
127}
128
129
130status_t
131IPPPrinterRoster::ListPorts(BMessage* msg)
132{
133	IPPPrinterMap::Iterator iterator = fPrinters.GetIterator();
134	while (iterator.HasNext()) {
135		const IPPPrinterMap::Entry& entry = iterator.Next();
136		msg->AddString("port_id", entry.value->fURI);
137
138		BString name = entry.value->fInfo;
139		if (name.Length() && entry.value->fLocation.Length()) {
140			name.Append(" [");
141			name.Append(entry.value->fLocation);
142			name.Append("]");
143		}
144
145		msg->AddString("port_name", name);
146	}
147
148	return B_OK;
149}
150
151
152char*
153IPPPrinterRoster::_ParseString(BString& outStr, char*& pos)
154{
155	outStr = "";
156
157	if (*pos == '"')
158		pos++;
159
160	while(*pos && *pos != '"')
161		outStr.Append(*pos++, 1);
162
163	if (*pos == '"')
164		++pos;
165
166	while(*pos && isspace(*pos))
167		++pos;
168
169	if (!*pos)
170		pos = NULL;
171
172	return pos;
173}
174
175
176status_t
177IPPPrinterRoster::_IPPListeningThread(void *cookie)
178{
179	return ((IPPPrinterRoster*)cookie)->Listen();
180}
181
182
183// --- general transport hooks
184
185extern "C" _EXPORT void
186exit_transport()
187{
188	DBGMSG(("> exit_transport\n"));
189	if (transport) {
190		delete transport;
191		transport = NULL;
192	}
193	DBGMSG(("< exit_transport\n"));
194}
195
196
197// List detected printers
198extern "C" _EXPORT status_t
199list_transport_ports(BMessage* msg)
200{
201	return gRoster.ListPorts(msg);
202}
203
204
205extern "C" _EXPORT BDataIO *
206init_transport(BMessage *msg)
207{
208	DBGMSG(("> init_transport\n"));
209
210	transport = new IppTransport(msg);
211
212	if (transport->fail()) {
213		exit_transport();
214	}
215
216	if (msg)
217		msg->what = 'okok';
218
219	DBGMSG(("< init_transport\n"));
220	return transport;
221}
222