xref: /haiku/src/apps/codycam/VideoConsumer.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 //	Copyright (c) 1998-99, Be Incorporated, All Rights Reserved.
2 //	SMS
3 //	VideoConsumer.cpp
4 
5 
6 #include "FileUploadClient.h"
7 #include "FtpClient.h"
8 #include "SftpClient.h"
9 #include "VideoConsumer.h"
10 
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <string.h>
15 
16 #include <Application.h>
17 #include <Buffer.h>
18 #include <BufferGroup.h>
19 #include <Catalog.h>
20 #include <Locale.h>
21 #include <MediaRoster.h>
22 #include <NodeInfo.h>
23 #include <scheduler.h>
24 #include <StringView.h>
25 #include <TimeSource.h>
26 #include <View.h>
27 
28 
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "VideoConsumer.cpp"
31 
32 #define M1 ((double)1000000.0)
33 #define JITTER		20000
34 
35 #define	FUNCTION	printf
36 #define ERROR		printf
37 #define PROGRESS	printf
38 #define LOOP		printf
39 
40 
41 static status_t SetFileType(BFile* file,  int32 translator, uint32 type);
42 
43 const media_raw_video_format vid_format = {29.97, 1, 0, 239,
44 	B_VIDEO_TOP_LEFT_RIGHT, 1, 1, {B_RGB16, 320, 240, 320 * 4, 0, 0}};
45 
46 
47 VideoConsumer::VideoConsumer(const char* name, BView* view,
48 	BStringView* statusLine,
49 	BMediaAddOn* addon, const uint32 internalId)
50 	: BMediaNode(name),
51 	BMediaEventLooper(),
52 	BBufferConsumer(B_MEDIA_RAW_VIDEO),
53 	fStatusLine(statusLine),
54 	fInternalID(internalId),
55 	fAddOn(addon),
56 	fConnectionActive(false),
57 	fMyLatency(20000),
58 	fWindow(NULL),
59 	fView(view),
60 	fOurBuffers(false),
61 	fBuffers(NULL),
62 	fTimeToFtp(false),
63 	fFtpComplete(true),
64 	fRate(1000000),
65 	fImageFormat(0),
66 	fTranslator(0),
67 	fUploadClient(0),
68 	fPassiveFtp(true)
69 {
70 	FUNCTION("VideoConsumer::VideoConsumer\n");
71 
72 	AddNodeKind(B_PHYSICAL_OUTPUT);
73 	SetEventLatency(0);
74 	fWindow = fView->Window();
75 
76 	for (uint32 j = 0; j < 3; j++) {
77 		fBitmap[j] = NULL;
78 		fBufferMap[j] = 0;
79 	}
80 
81 	strcpy(fFileNameText, "");
82 	strcpy(fServerText, "");
83 	strcpy(fLoginText, "");
84 	strcpy(fPasswordText, "");
85 	strcpy(fDirectoryText, "");
86 
87 	SetPriority(B_DISPLAY_PRIORITY);
88 }
89 
90 
91 VideoConsumer::~VideoConsumer()
92 {
93 	FUNCTION("VideoConsumer::~VideoConsumer\n");
94 
95 	Quit();
96 
97 	if (fWindow) {
98 		puts(B_TRANSLATE("Locking the window"));
99 		if (fWindow->Lock()) {
100 			puts(B_TRANSLATE("Closing the window"));
101 			fWindow->Close();
102 			fWindow = 0;
103 		}
104 	}
105 
106 	// clean up ftp thread
107 	// wait up to 30 seconds if ftp is in progress
108 	int32 count = 0;
109 	while (!fFtpComplete && count < 30) {
110 		snooze(1000000);
111 		count++;
112 	}
113 
114 	if (count == 30)
115 		kill_thread(fFtpThread);
116 
117 	DeleteBuffers();
118 
119 }
120 
121 /********************************
122 	From BMediaNode
123 ********************************/
124 
125 
126 BMediaAddOn*
127 VideoConsumer::AddOn(int32* cookie) const
128 {
129 	FUNCTION("VideoConsumer::AddOn\n");
130 	// do the right thing if we're ever used with an add-on
131 	*cookie = fInternalID;
132 	return fAddOn;
133 }
134 
135 
136 // This implementation is required to get around a bug in
137 // the ppc compiler.
138 
139 void
140 VideoConsumer::Start(bigtime_t performanceTime)
141 {
142 	BMediaEventLooper::Start(performanceTime);
143 }
144 
145 
146 void
147 VideoConsumer::Stop(bigtime_t performanceTime, bool immediate)
148 {
149 	BMediaEventLooper::Stop(performanceTime, immediate);
150 }
151 
152 
153 void
154 VideoConsumer::Seek(bigtime_t mediaTime, bigtime_t performanceTime)
155 {
156 	BMediaEventLooper::Seek(mediaTime, performanceTime);
157 }
158 
159 
160 void
161 VideoConsumer::TimeWarp(bigtime_t atRealTime, bigtime_t toPerformanceTime)
162 {
163 	BMediaEventLooper::TimeWarp(atRealTime, toPerformanceTime);
164 }
165 
166 
167 status_t
168 VideoConsumer::DeleteHook(BMediaNode* node)
169 {
170 	return BMediaEventLooper::DeleteHook(node);
171 }
172 
173 
174 void
175 VideoConsumer::NodeRegistered()
176 {
177 	FUNCTION("VideoConsumer::NodeRegistered\n");
178 	fIn.destination.port = ControlPort();
179 	fIn.destination.id = 0;
180 	fIn.source = media_source::null;
181 	fIn.format.type = B_MEDIA_RAW_VIDEO;
182 	fIn.format.u.raw_video = vid_format;
183 
184 	Run();
185 }
186 
187 
188 status_t
189 VideoConsumer::RequestCompleted(const media_request_info& info)
190 {
191 	FUNCTION("VideoConsumer::RequestCompleted\n");
192 	switch (info.what) {
193 		case media_request_info::B_SET_OUTPUT_BUFFERS_FOR:
194 			if (info.status != B_OK)
195 					ERROR("VideoConsumer::RequestCompleted: Not using our buffers!\n");
196 			break;
197 
198 		default:
199 			ERROR("VideoConsumer::RequestCompleted: Invalid argument\n");
200 			break;
201 	}
202 	return B_OK;
203 }
204 
205 
206 status_t
207 VideoConsumer::HandleMessage(int32 message, const void* data, size_t size)
208 {
209 	//FUNCTION("VideoConsumer::HandleMessage\n");
210 	ftp_msg_info* info = (ftp_msg_info*)data;
211 	status_t status = B_OK;
212 
213 	switch (message) {
214 		case FTP_INFO:
215 			PROGRESS("VideoConsumer::HandleMessage - FTP_INFO message\n");
216 			fRate = info->rate;
217 			fImageFormat = info->imageFormat;
218 			fTranslator = info->translator;
219 			fPassiveFtp = info->passiveFtp;
220 			fUploadClient = info->uploadClient;
221 			strcpy(fFileNameText, info->fileNameText);
222 			strcpy(fServerText, info->serverText);
223 			strcpy(fLoginText, info->loginText);
224 			strcpy(fPasswordText, info->passwordText);
225 			strcpy(fDirectoryText, info->directoryText);
226 			// remove old user events
227 			EventQueue()->FlushEvents(TimeSource()->Now(), BTimedEventQueue::B_ALWAYS,
228 				true, BTimedEventQueue::B_USER_EVENT);
229 			if (fRate != B_INFINITE_TIMEOUT) {
230 				// if rate is not "Never," push an event
231 				// to restart captures 5 seconds from now
232 				media_timed_event event(TimeSource()->Now() + 5000000,
233 					BTimedEventQueue::B_USER_EVENT);
234 				EventQueue()->AddEvent(event);
235 			}
236 			break;
237 	}
238 
239 	return status;
240 }
241 
242 
243 void
244 VideoConsumer::BufferReceived(BBuffer* buffer)
245 {
246 	LOOP("VideoConsumer::Buffer #%" B_PRId32 " received, start_time %" B_PRIdBIGTIME
247 		"\n", buffer->ID(), buffer->Header()->start_time);
248 
249 	if (RunState() == B_STOPPED) {
250 		buffer->Recycle();
251 		return;
252 	}
253 
254 	media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
255 		buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
256 	EventQueue()->AddEvent(event);
257 }
258 
259 
260 void
261 VideoConsumer::ProducerDataStatus(const media_destination& forWhom, int32 status,
262 	bigtime_t atMediaTime)
263 {
264 	FUNCTION("VideoConsumer::ProducerDataStatus\n");
265 
266 	if (forWhom != fIn.destination)
267 		return;
268 }
269 
270 
271 status_t
272 VideoConsumer::CreateBuffers(const media_format& withFormat)
273 {
274 	FUNCTION("VideoConsumer::CreateBuffers\n");
275 
276 	DeleteBuffers();
277 		// delete any old buffers
278 
279 	status_t status = B_OK;
280 
281 	// create a buffer group
282 	uint32 xSize = withFormat.u.raw_video.display.line_width;
283 	uint32 ySize = withFormat.u.raw_video.display.line_count;
284 	color_space colorspace = withFormat.u.raw_video.display.format;
285 	PROGRESS("VideoConsumer::CreateBuffers - Colorspace = %d\n", colorspace);
286 
287 	fBuffers = new BBufferGroup();
288 	status = fBuffers->InitCheck();
289 	if (status != B_OK) {
290 		ERROR("VideoConsumer::CreateBuffers - ERROR CREATING BUFFER GROUP\n");
291 		return status;
292 	}
293 	// and attach the  bitmaps to the buffer group
294 	for (uint32 j = 0; j < 3; j++) {
295 		fBitmap[j] = new BBitmap(BRect(0, 0, (xSize - 1), (ySize - 1)), colorspace,
296 			false, true);
297 		if (fBitmap[j]->IsValid()) {
298 			buffer_clone_info info;
299 			if ((info.area = area_for(fBitmap[j]->Bits())) == B_ERROR)
300 				ERROR("VideoConsumer::CreateBuffers - ERROR IN AREA_FOR\n");
301 			info.offset = 0;
302 			info.size = (size_t)fBitmap[j]->BitsLength();
303 			info.flags = j;
304 			info.buffer = 0;
305 
306 			if ((status = fBuffers->AddBuffer(info)) != B_OK) {
307 				ERROR("VideoConsumer::CreateBuffers - ERROR ADDING BUFFER TO GROUP\n");
308 				return status;
309 			}
310 			else
311 				PROGRESS("VideoConsumer::CreateBuffers - SUCCESSFUL ADD BUFFER TO GROUP\n");
312 		} else {
313 			ERROR("VideoConsumer::CreateBuffers - ERROR CREATING VIDEO RING "
314 				"BUFFER: %08" B_PRIx32 "\n", status);
315 			return B_ERROR;
316 		}
317 	}
318 
319 	BBuffer* buffList[3];
320 	for (int j = 0; j < 3; j++)
321 		buffList[j] = NULL;
322 
323 	if ((status = fBuffers->GetBufferList(3, buffList)) == B_OK)
324 		for (int j = 0; j < 3; j++)
325 			if (buffList[j] != NULL) {
326 				fBufferMap[j] = buffList[j];
327 				PROGRESS(" j = %d buffer = %p\n", j, fBufferMap[j]);
328 			} else {
329 				ERROR("VideoConsumer::CreateBuffers ERROR MAPPING RING BUFFER\n");
330 				return B_ERROR;
331 			}
332 	else
333 		ERROR("VideoConsumer::CreateBuffers ERROR IN GET BUFFER LIST\n");
334 
335 	fFtpBitmap = new BBitmap(BRect(0, 0, xSize - 1, ySize - 1), B_RGB32, false, false);
336 
337 	FUNCTION("VideoConsumer::CreateBuffers - EXIT\n");
338 	return status;
339 }
340 
341 
342 void
343 VideoConsumer::DeleteBuffers()
344 {
345 	FUNCTION("VideoConsumer::DeleteBuffers\n");
346 
347 	if (fBuffers) {
348 		delete fBuffers;
349 		fBuffers = NULL;
350 
351 		for (uint32 j = 0; j < 3; j++)
352 			if (fBitmap[j]->IsValid()) {
353 				delete fBitmap[j];
354 				fBitmap[j] = NULL;
355 			}
356 	}
357 	FUNCTION("VideoConsumer::DeleteBuffers - EXIT\n");
358 }
359 
360 
361 status_t
362 VideoConsumer::Connected(const media_source& producer, const media_destination& where,
363 	const media_format& withFormat, media_input* outInput)
364 {
365 	FUNCTION("VideoConsumer::Connected\n");
366 
367 	fIn.source = producer;
368 	fIn.format = withFormat;
369 	fIn.node = Node();
370 	sprintf(fIn.name, "Video Consumer");
371 	*outInput = fIn;
372 
373 	uint32 userData = 0;
374 	int32 changeTag = 1;
375 	if (CreateBuffers(withFormat) == B_OK)
376 		BBufferConsumer::SetOutputBuffersFor(producer, fDestination,
377 			fBuffers, (void *)&userData, &changeTag, true);
378 	else {
379 		ERROR("VideoConsumer::Connected - COULDN'T CREATE BUFFERS\n");
380 		return B_ERROR;
381 	}
382 
383 	fConnectionActive = true;
384 
385 	FUNCTION("VideoConsumer::Connected - EXIT\n");
386 	return B_OK;
387 }
388 
389 
390 void
391 VideoConsumer::Disconnected(const media_source& producer, const media_destination& where)
392 {
393 	FUNCTION("VideoConsumer::Disconnected\n");
394 
395 	if (where == fIn.destination && producer == fIn.source) {
396 		// disconnect the connection
397 		fIn.source = media_source::null;
398 		delete fFtpBitmap;
399 		fConnectionActive = false;
400 	}
401 
402 }
403 
404 
405 status_t
406 VideoConsumer::AcceptFormat(const media_destination& dest, media_format* format)
407 {
408 	FUNCTION("VideoConsumer::AcceptFormat\n");
409 
410 	if (dest != fIn.destination) {
411 		ERROR("VideoConsumer::AcceptFormat - BAD DESTINATION\n");
412 		return B_MEDIA_BAD_DESTINATION;
413 	}
414 
415 	if (format->type == B_MEDIA_NO_TYPE)
416 		format->type = B_MEDIA_RAW_VIDEO;
417 
418 	if (format->type != B_MEDIA_RAW_VIDEO) {
419 		ERROR("VideoConsumer::AcceptFormat - BAD FORMAT\n");
420 		return B_MEDIA_BAD_FORMAT;
421 	}
422 
423 	if (format->u.raw_video.display.format != B_RGB32
424 		&& format->u.raw_video.display.format != B_RGB16
425 		&& format->u.raw_video.display.format != B_RGB15
426 		&& format->u.raw_video.display.format != B_GRAY8
427 		&&
428 		format->u.raw_video.display.format != media_raw_video_format::wildcard.display.format) {
429 		ERROR("AcceptFormat - not a format we know about!\n");
430 		return B_MEDIA_BAD_FORMAT;
431 	}
432 
433 	if (format->u.raw_video.display.format == media_raw_video_format::wildcard.display.format) {
434 		format->u.raw_video.display.format = B_RGB16;
435 	}
436 
437 	char formatString[256];
438 	string_for_format(*format, formatString, 256);
439 	FUNCTION("VideoConsumer::AcceptFormat: %s\n", formatString);
440 
441 	return B_OK;
442 }
443 
444 
445 status_t
446 VideoConsumer::GetNextInput(int32* cookie, media_input* outInput)
447 {
448 	FUNCTION("VideoConsumer::GetNextInput\n");
449 
450 	// custom build a destination for this connection
451 	// put connection number in id
452 
453 	if (*cookie < 1) {
454 		fIn.node = Node();
455 		fIn.destination.id = *cookie;
456 		sprintf(fIn.name, "Video Consumer");
457 		*outInput = fIn;
458 		(*cookie)++;
459 		return B_OK;
460 	} else {
461 		ERROR("VideoConsumer::GetNextInput - - BAD INDEX\n");
462 		return B_MEDIA_BAD_DESTINATION;
463 	}
464 }
465 
466 
467 void
468 VideoConsumer::DisposeInputCookie(int32 /*cookie*/)
469 {
470 }
471 
472 
473 status_t
474 VideoConsumer::GetLatencyFor(const media_destination& forWhom, bigtime_t* outLatency,
475 	media_node_id* out_timesource)
476 {
477 	FUNCTION("VideoConsumer::GetLatencyFor\n");
478 
479 	if (forWhom != fIn.destination)
480 		return B_MEDIA_BAD_DESTINATION;
481 
482 	*outLatency = fMyLatency;
483 	*out_timesource = TimeSource()->ID();
484 	return B_OK;
485 }
486 
487 
488 status_t
489 VideoConsumer::FormatChanged(const media_source& producer, const media_destination& consumer,
490 	int32 fromChangeCount, const media_format& format)
491 {
492 	FUNCTION("VideoConsumer::FormatChanged\n");
493 
494 	if (consumer != fIn.destination)
495 		return B_MEDIA_BAD_DESTINATION;
496 
497 	if (producer != fIn.source)
498 		return B_MEDIA_BAD_SOURCE;
499 
500 	fIn.format = format;
501 
502 	return CreateBuffers(format);
503 }
504 
505 
506 void
507 VideoConsumer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
508 	bool realTimeEvent)
509 {
510 	LOOP("VideoConsumer::HandleEvent\n");
511 
512 	BBuffer* buffer;
513 
514 	switch (event->type) {
515 		case BTimedEventQueue::B_START:
516 			PROGRESS("VideoConsumer::HandleEvent - START\n");
517 			break;
518 
519 		case BTimedEventQueue::B_STOP:
520 			PROGRESS("VideoConsumer::HandleEvent - STOP\n");
521 			EventQueue()->FlushEvents(event->event_time, BTimedEventQueue::B_ALWAYS,
522 				true, BTimedEventQueue::B_HANDLE_BUFFER);
523 			break;
524 
525 		case BTimedEventQueue::B_USER_EVENT:
526 			PROGRESS("VideoConsumer::HandleEvent - USER EVENT\n");
527 			if (RunState() == B_STARTED) {
528 				fTimeToFtp = true;
529 				PROGRESS("Pushing user event for %.4f, time now %.4f\n",
530 					(event->event_time + fRate) / M1, event->event_time/M1);
531 				media_timed_event newEvent(event->event_time + fRate,
532 					BTimedEventQueue::B_USER_EVENT);
533 				EventQueue()->AddEvent(newEvent);
534 			}
535 			break;
536 
537 		case BTimedEventQueue::B_HANDLE_BUFFER:
538 		{
539 			LOOP("VideoConsumer::HandleEvent - HANDLE BUFFER\n");
540 			buffer = (BBuffer *)event->pointer;
541 			if (RunState() == B_STARTED && fConnectionActive) {
542 				// see if this is one of our buffers
543 				uint32 index = 0;
544 				fOurBuffers = true;
545 				while (index < 3)
546 					if (buffer == fBufferMap[index])
547 						break;
548 					else
549 						index++;
550 
551 				if (index == 3) {
552 					// no, buffers belong to consumer
553 					fOurBuffers = false;
554 					index = 0;
555 				}
556 
557 				if (fFtpComplete && fTimeToFtp) {
558 					PROGRESS("VidConsumer::HandleEvent - SPAWNING FTP THREAD\n");
559 					fTimeToFtp = false;
560 					fFtpComplete = false;
561 					memcpy(fFtpBitmap->Bits(), buffer->Data(), fFtpBitmap->BitsLength());
562 					fFtpThread = spawn_thread(FtpRun, "Video Window Ftp", B_NORMAL_PRIORITY, this);
563 					resume_thread(fFtpThread);
564 				}
565 
566 				if ((RunMode() == B_OFFLINE)
567 					|| ((TimeSource()->Now() > (buffer->Header()->start_time - JITTER))
568 						&& (TimeSource()->Now() < (buffer->Header()->start_time + JITTER)))) {
569 					if (!fOurBuffers)
570 						// not our buffers, so we need to copy
571 						memcpy(fBitmap[index]->Bits(), buffer->Data(), fBitmap[index]->BitsLength());
572 
573 					if (fWindow->Lock()) {
574 						uint32 flags;
575 						if ((fBitmap[index]->ColorSpace() == B_GRAY8) &&
576 							!bitmaps_support_space(fBitmap[index]->ColorSpace(), &flags)) {
577 							// handle mapping of GRAY8 until app server knows how
578 							uint32* start = (uint32*)fBitmap[index]->Bits();
579 							int32 size = fBitmap[index]->BitsLength();
580 							uint32* end = start + size / 4;
581 							for (uint32* p = start; p < end; p++)
582 								*p = (*p >> 3) & 0x1f1f1f1f;
583 						}
584 
585 						fView->DrawBitmap(fBitmap[index], fView->Bounds());
586 						fWindow->Unlock();
587 					}
588 				}
589 				else
590 					PROGRESS("VidConsumer::HandleEvent - DROPPED FRAME\n");
591 				buffer->Recycle();
592 			}
593 			else
594 				buffer->Recycle();
595 			break;
596 		}
597 
598 		default:
599 			ERROR("VideoConsumer::HandleEvent - BAD EVENT\n");
600 			break;
601 	}
602 }
603 
604 
605 status_t
606 VideoConsumer::FtpRun(void* data)
607 {
608 	FUNCTION("VideoConsumer::FtpRun\n");
609 
610 	((VideoConsumer *)data)->FtpThread();
611 
612 	return 0;
613 }
614 
615 
616 void
617 VideoConsumer::FtpThread()
618 {
619 	char fullPath[B_PATH_NAME_LENGTH];
620 	FUNCTION("VideoConsumer::FtpThread\n");
621 	if (fUploadClient == 2) {
622 		// 64 + 64 = 128 max
623 		snprintf(fullPath, B_PATH_NAME_LENGTH, "%s/%s", fDirectoryText, fFileNameText);
624 		LocalSave(fullPath, fFtpBitmap);
625 	} else if (LocalSave(fFileNameText, fFtpBitmap) == B_OK)
626 		FtpSave(fFileNameText);
627 
628 #if 0
629 	// save a small version, too
630 	BBitmap* b = new BBitmap(BRect(0,0,159,119), B_RGB32, true, false);
631 	BView* v = new BView(BRect(0,0,159,119), "SmallView 1", 0, B_WILL_DRAW);
632 	b->AddChild(v);
633 
634 	b->Lock();
635 	v->DrawBitmap(fFtpBitmap, v->Frame());
636 	v->Sync();
637 	b->Unlock();
638 
639 	if (LocalSave("small.jpg", b) == B_OK)
640 		FtpSave("small.jpg");
641 
642 	delete b;
643 #endif
644 
645 	fFtpComplete = true;
646 }
647 
648 
649 void
650 VideoConsumer::UpdateFtpStatus(const char* status)
651 {
652 	printf("FTP STATUS: %s\n",status);
653 	if (fView->Window()->Lock()) {
654 		fStatusLine->SetText(status);
655 		fView->Window()->Unlock();
656 	}
657 }
658 
659 
660 status_t
661 VideoConsumer::LocalSave(char* filename, BBitmap* bitmap)
662 {
663 	BFile* output;
664 
665 	UpdateFtpStatus(B_TRANSLATE("Capturing Image" B_UTF8_ELLIPSIS));
666 
667 	/* save a local copy of the image in the requested format */
668 	output = new BFile();
669 	if (output->SetTo(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE) == B_NO_ERROR) {
670 		BBitmapStream input(bitmap);
671 		status_t err = BTranslatorRoster::Default()->Translate(&input, NULL, NULL,
672 			output, fImageFormat);
673 		if (err == B_OK) {
674 			err = SetFileType(output, fTranslator, fImageFormat);
675 			if (err != B_OK)
676 				UpdateFtpStatus(B_TRANSLATE("Error setting type of output file"));
677 		}
678 		else
679 			UpdateFtpStatus(B_TRANSLATE("Error writing output file"));
680 
681 		input.DetachBitmap(&bitmap);
682 		output->Unset();
683 		delete output;
684 		return B_OK;
685 	} else {
686 		UpdateFtpStatus(B_TRANSLATE("Error creating output file"));
687 		return B_ERROR;
688 	}
689 }
690 
691 
692 status_t
693 VideoConsumer::FtpSave(char* filename)
694 {
695 	FileUploadClient *ftp;
696 
697 	//XXX: make that cleaner
698 	switch (fUploadClient) {
699 		case 0:
700 			ftp = new FtpClient;
701 			break;
702 		case 1:
703 			ftp = new SftpClient;
704 			break;
705 		case 2:
706 			return B_OK;
707 		default:
708 			fprintf(stderr, B_TRANSLATE("invalid upload client %ld\n"),
709 				fUploadClient);
710 			return EINVAL;
711 	}
712 
713 	ftp->SetPassive(fPassiveFtp);
714 		// ftp the local file to our web site
715 
716 	UpdateFtpStatus(B_TRANSLATE("Logging in" B_UTF8_ELLIPSIS));
717 	if (ftp->Connect((string)fServerText, (string)fLoginText,
718 		(string)fPasswordText)) {
719 		// connect to server
720 		UpdateFtpStatus(B_TRANSLATE("Connected" B_UTF8_ELLIPSIS));
721 
722 		if (ftp->ChangeDir((string)fDirectoryText)) {
723 			// cd to the desired directory
724 			UpdateFtpStatus(B_TRANSLATE("Upload" B_UTF8_ELLIPSIS));
725 
726 			if (ftp->PutFile((string)filename, (string)"temp")) {
727 				// send the file to the server
728 
729 				ftp->Chmod((string)"temp", (string)"644");
730 				// make it world readable
731 
732 				UpdateFtpStatus(B_TRANSLATE("Renaming" B_UTF8_ELLIPSIS));
733 
734 				if (ftp->MoveFile((string)"temp", (string)filename)) {
735 					// change to the desired name
736 					uint32 time = real_time_clock();
737 					char s[80];
738 					strcpy(s, B_TRANSLATE("Last Capture: "));
739 					strcat(s, ctime((const time_t*)&time));
740 					s[strlen(s) - 1] = 0;
741 					UpdateFtpStatus(s);
742 					delete ftp;
743 					return B_OK;
744 				} else
745 					UpdateFtpStatus(B_TRANSLATE("Rename failed"));
746 			} else
747 				UpdateFtpStatus(B_TRANSLATE("File upload failed"));
748 		} else
749 			UpdateFtpStatus(B_TRANSLATE("Couldn't find requested directory on "
750 				"server"));
751 	}
752 	else
753 		UpdateFtpStatus(B_TRANSLATE("Server login failed"));
754 
755 	delete ftp;
756 	return B_ERROR;
757 }
758 
759 
760 status_t
761 SetFileType(BFile* file, int32 translator, uint32 type)
762 {
763 	translation_format* formats;
764 	int32 count;
765 
766 	status_t err = BTranslatorRoster::Default()->GetOutputFormats(translator,
767 		(const translation_format **)&formats, &count);
768 	if (err < B_OK)
769 		return err;
770 
771 	const char* mime = NULL;
772 	for (int ix = 0; ix < count; ix++) {
773 		if (formats[ix].type == type) {
774 			mime = formats[ix].MIME;
775 			break;
776 		}
777 	}
778 
779 	if (mime == NULL) {
780 		/* this should not happen, but being defensive might be prudent */
781 		return B_ERROR;
782 	}
783 
784 	/* use BNodeInfo to set the file type */
785 	BNodeInfo ninfo(file);
786 	return ninfo.SetType(mime);
787 }
788