Warning: Can't synchronize with repository "(default)" (No changeset 96d22ec3fa3ef6de3ea8dc0d7d398adc9aa071cf in the repository). Look in the Trac log for more information.

source: fsogsmd/src/plugins/modem_dummy/mediators.vala @ dd9f4c14

Revision dd9f4c14, 20.1 KB checked in by Klaus Kurzmann <mok@…>, 2 years ago (diff)

fsogsmd: add vim tags for correct indentation with 4 spaces

Signed-off-by: Klaus Kurzmann <mok@…>

  • Property mode set to 100644
Line 
1/*
2 * Copyright (C) 2009-2011 Michael 'Mickey' Lauer <mlauer@vanille-media.de>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (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
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17 *
18 */
19
20/**
21 * This file contains Dbus/AT command mediators only using official 3GPP AT commands.
22 *
23 * Do _not_ add vendor-specific mediators here, instead add them to your modem plugin.
24 **/
25
26using Gee;
27
28namespace FsoGsm {
29
30public bool modem_microphoneMuted;
31public int modem_speakerVolume;
32public string modem_pin;
33public bool modem_unlocked;
34public bool modem_haspin;
35public string modem_scsa;
36
37/**
38 * Debug Mediators
39 **/
40public class DummyAtDebugCommand : DebugCommand
41{
42    public override async void run( string command, string category ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
43    {
44        this.response = "OK";
45    }
46}
47
48public class DummyAtDebugInjectResponse : DebugInjectResponse
49{
50    public override async void run( string command, string category ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
51    {
52    }
53}
54
55public class DummyAtDebugPing : DebugPing
56{
57    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
58    {
59    }
60}
61
62/**
63 * Device Mediators
64 **/
65public class DummyAtDeviceGetAlarmTime : DeviceGetAlarmTime
66{
67    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
68    {
69        since_epoch = 0;
70    }
71}
72
73public class DummyAtDeviceGetCurrentTime : DeviceGetCurrentTime
74{
75    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
76    {
77        var t = GLib.Time();
78        since_epoch = (int) Linux.timegm( t );
79    }
80}
81
82public class DummyAtDeviceGetFunctionality : DeviceGetFunctionality
83{
84    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
85    {
86        level = "full";
87        autoregister = theModem.data().keepRegistration;
88        pin = theModem.data().simPin;
89    }
90}
91
92public class DummyAtDeviceGetInformation : DeviceGetInformation
93{
94    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
95    {
96        info = new GLib.HashTable<string,Variant>( str_hash, str_equal );
97        info.insert( "model", "FSO2 Dummy Modem" );
98        info.insert( "manufacturer", "freesmartphone.org" );
99        info.insert( "revision", "V2" );
100        info.insert( "imei", "1234567890123456" );
101    }
102}
103
104public class DummyAtDeviceGetFeatures : DeviceGetFeatures
105{
106    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
107    {
108        features = new GLib.HashTable<string,Variant>( str_hash, str_equal );
109        features.insert( "voice", true );
110        features.insert( "csd", true );
111        features.insert( "gsm", true );
112        features.insert( "pdp", "B" );
113        features.insert( "fax", "8" );
114        features.insert( "facilities", "SM" );
115    }
116}
117
118public class DummyAtDeviceGetMicrophoneMuted : DeviceGetMicrophoneMuted
119{
120    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
121    {
122        muted = modem_microphoneMuted;
123    }
124}
125
126public class DummyAtDeviceGetSpeakerVolume : DeviceGetSpeakerVolume
127{
128    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
129    {
130        volume = modem_speakerVolume;
131    }
132}
133
134public class DummyAtDeviceGetPowerStatus : DeviceGetPowerStatus
135{
136    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
137    {
138        status = FreeSmartphone.Device.PowerStatus.DISCHARGING;
139        level = 42;
140    }
141}
142
143public class DummyAtDeviceSetAlarmTime : DeviceSetAlarmTime
144{
145    public override async void run( int since_epoch ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
146    {
147    }
148}
149
150public class DummyAtDeviceSetCurrentTime : DeviceSetCurrentTime
151{
152    public override async void run( int since_epoch ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
153    {
154    }
155}
156
157public class DummyAtDeviceSetFunctionality : DeviceSetFunctionality
158{
159    public override async void run( string level, bool autoregister, string pin ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
160    {
161        Timeout.add_seconds( 3, run.callback );
162        yield;
163
164        if ( modem_pin != pin )
165        {
166            var simiface = theModem.theDevice<FreeSmartphone.GSM.SIM>();
167            simiface.auth_status( FreeSmartphone.GSM.SIMAuthStatus.PIN_REQUIRED );
168        }
169    }
170}
171
172public class DummyAtDeviceSetMicrophoneMuted : DeviceSetMicrophoneMuted
173{
174    public override async void run( bool muted ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
175    {
176        modem_microphoneMuted = muted;
177    }
178}
179
180public class DummyAtDeviceSetSpeakerVolume : DeviceSetSpeakerVolume
181{
182    public override async void run( int volume ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
183    {
184        modem_speakerVolume = volume;
185    }
186}
187
188/**
189 * SIM Mediators
190 **/
191public class DummyAtSimChangeAuthCode : SimChangeAuthCode
192{
193    public override async void run( string oldpin, string newpin ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
194    {
195        if ( oldpin != modem_pin )
196        {
197            throw new FreeSmartphone.GSM.Error.SIM_AUTH_FAILED( "Wrong PIN" );
198        }
199        modem_pin = newpin;
200    }
201}
202
203public class DummyAtSimDeleteEntry : SimDeleteEntry
204{
205    public override async void run( string category, int index ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
206    {
207    }
208}
209
210public class DummyAtSimDeleteMessage : SimDeleteMessage
211{
212    public override async void run( int index ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
213    {
214    }
215}
216
217public class DummyAtSimGetAuthStatus : SimGetAuthStatus
218{
219    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
220    {
221        status = modem_unlocked ? FreeSmartphone.GSM.SIMAuthStatus.READY : FreeSmartphone.GSM.SIMAuthStatus.PIN_REQUIRED;
222    }
223}
224
225public class DummyAtSimGetInformation : SimGetInformation
226{
227    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
228    {
229        info = new GLib.HashTable<string,Variant>( str_hash, str_equal );
230        info.insert( "imsi", "262123456789" );
231        info.insert( "issuer", "FSO TELEKOM" );
232        info.insert( "slots", 30 );
233        info.insert( "message", 4 );
234        info.insert( "phonebooks", "contacts" );
235    }
236}
237
238public class DummyAtSimGetAuthCodeRequired : SimGetAuthCodeRequired
239{
240    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
241    {
242        required = modem_haspin;
243    }
244}
245
246public class DummyAtSimGetServiceCenterNumber : SimGetServiceCenterNumber
247{
248    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
249    {
250        number = modem_scsa;
251    }
252}
253
254public class DummyAtSimRetrieveMessage : SimRetrieveMessage
255{
256    public override async void run( int index, out string status, out string number, out string contents, out GLib.HashTable<string,GLib.Variant> properties ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
257    {
258        status = "unknown";
259        number = "unknown";
260        contents = "unknown";
261        properties = new GLib.HashTable<string,GLib.Variant>( GLib.str_hash, GLib.str_equal );
262    }
263}
264
265public class DummyAtSimRetrievePhonebook : SimRetrievePhonebook
266{
267    public override async void run( string category, int mindex, int maxdex ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
268    {
269        if ( ! ( category == "contacts" ) )
270        {
271            throw new FreeSmartphone.Error.INVALID_PARAMETER( "Unknown category" );
272        }
273
274        var pb = new FreeSmartphone.GSM.SIMEntry[] {};
275
276        if ( mindex >= 1 && maxdex <= 1 )
277            pb += FreeSmartphone.GSM.SIMEntry( 1, "Dr. Mickey Lauer", "+4912345678" );
278        if ( mindex >= 2 && maxdex <= 2 )
279            pb += FreeSmartphone.GSM.SIMEntry( 2, "Dr. Sabine Lauer", "+4976543210" );
280        if ( mindex >= 3 && maxdex <= 3 )
281            pb += FreeSmartphone.GSM.SIMEntry( 3, "Daniel Willmann", "+4976543210" );
282        if ( mindex >= 4 && maxdex <= 4 )
283            pb += FreeSmartphone.GSM.SIMEntry( 4, "Jan LÃŒbbe", "+4976543210" );
284        if ( mindex >= 5 && maxdex <= 5 )
285            pb += FreeSmartphone.GSM.SIMEntry( 5, "Stefan Schmidt", "+497655543210" );
286        if ( mindex >= 6 && maxdex <= 6 )
287            pb += FreeSmartphone.GSM.SIMEntry( 6, "Frederik Sdun", "+497651243210" );
288        if ( mindex >= 7 && maxdex <= 7 )
289            pb += FreeSmartphone.GSM.SIMEntry( 7, "Simon Busch", "+497116543210" );
290        if ( mindex >= 8 && maxdex <= 8 )
291            pb += FreeSmartphone.GSM.SIMEntry( 8, "Mr. Moku", "+492376543210" );
292        if ( mindex >= 9 && maxdex <= 9 )
293            pb += FreeSmartphone.GSM.SIMEntry( 9, "Hans Wurst", "+493376543210" );
294        if ( mindex >= 10 && maxdex <= 10 )
295            pb += FreeSmartphone.GSM.SIMEntry( 10, "Prof. Med. Wurst", "+493376543210" );
296        if ( mindex >= 11 && maxdex <= 11 )
297            pb += FreeSmartphone.GSM.SIMEntry( 11, "Wer 'auch' immer", "+4971236543210" );
298        if ( mindex >= 12 && maxdex <= 12 )
299            pb += FreeSmartphone.GSM.SIMEntry( 12, "Sir Lancelot", "+1555543210" );
300        if ( mindex >= 13 && maxdex <= 13 )
301            pb += FreeSmartphone.GSM.SIMEntry( 13, "Merlin", "+410001w552w455543210" );
302
303        this.phonebook = pb;
304    }
305}
306
307public class DummyAtSimSetAuthCodeRequired : SimSetAuthCodeRequired
308{
309    public override async void run( bool required, string pin ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
310    {
311    }
312}
313
314public class DummyAtSimSendAuthCode : SimSendAuthCode
315{
316    public override async void run( string pin ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
317    {
318        if ( pin != modem_pin )
319        {
320            throw new FreeSmartphone.GSM.Error.SIM_AUTH_FAILED( @"PIN $pin not accepted" );
321        }
322        theModem.advanceToState( Modem.Status.ALIVE_SIM_UNLOCKED );
323    }
324}
325
326public class DummyAtSimSetServiceCenterNumber : SimSetServiceCenterNumber
327{
328    public override async void run( string number ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
329    {
330        modem_scsa = number;
331    }
332}
333
334public class DummyAtSimWriteEntry : SimWriteEntry
335{
336    public override async void run( string category, int index, string number, string name ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
337    {
338    }
339}
340
341public class DummyAtSimUnlock : SimUnlock
342{
343    public override async void run( string puk, string newpin ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
344    {
345        modem_pin = newpin;
346    }
347}
348
349/**
350 * SMS Mediators
351 **/
352public class DummyAtSmsSendTextMessage : SmsSendTextMessage
353{
354    public override async void run( string recipient_number, string contents, bool want_report ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
355    {
356        transaction_index = 1;
357        timestamp = "now";
358    }
359}
360
361public class DummyAtSmsRetrieveTextMessages : SmsRetrieveTextMessages
362{
363    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
364    {
365        var mb = new FreeSmartphone.GSM.SIMMessage[] {};
366        var props = new GLib.HashTable<string,GLib.Variant>( GLib.str_hash, GLib.str_equal );
367
368        mb += FreeSmartphone.GSM.SIMMessage( 1, "single", "+123456789", "Yo, what's up in da house tonight?", "timestamp", props );
369        mb += FreeSmartphone.GSM.SIMMessage( 2, "single", "+555456789", "It's going to be cold, don't forget your coat, sun!", "timestamp", props );
370        mb += FreeSmartphone.GSM.SIMMessage( 3, "single", "+123456789", "And I thought you loved me :(", "timestamp", props );
371        mb += FreeSmartphone.GSM.SIMMessage( 4, "single", "+555456789", "Don't forget to bring Dad's medicine", "timestamp", props );
372
373        this.messagebook = mb;
374    }
375}
376
377/**
378 * Network Mediators
379 **/
380public class DummyAtNetworkGetSignalStrength : NetworkGetSignalStrength
381{
382    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
383    {
384        signal = 50;
385    }
386}
387
388public class DummyAtNetworkGetStatus : NetworkGetStatus
389{
390    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
391    {
392        status = new GLib.HashTable<string,Variant>( str_hash, str_equal );
393        status.insert( "strength", 50 );
394        status.insert( "registration", "home" );
395        status.insert( "lac", "F71A" );
396        status.insert( "cid", "AB12" );
397
398        status.insert( "mode", "home" );
399        status.insert( "provider", "FSO TELEKOM" );
400        status.insert( "act", "EDGE" );
401
402        status.insert( "code", "262171" );
403    }
404}
405
406public class DummyAtNetworkListProviders : NetworkListProviders
407{
408    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
409    {
410        //providers = cmd.providers;
411    }
412}
413
414public class DummyAtNetworkRegister : NetworkRegister
415{
416    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
417    {
418        Timeout.add_seconds( 5, run.callback );
419        yield;
420    }
421}
422
423/**
424 * Call Mediators
425 **/
426public class DummyAtCallActivate : CallActivate
427{
428    public override async void run( int id ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
429    {
430    }
431}
432
433public class DummyAtCallHoldActive : CallHoldActive
434{
435    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
436    {
437    }
438}
439
440public class DummyAtCallInitiate : CallInitiate
441{
442    public override async void run( string number, string ctype ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
443    {
444    }
445}
446
447public class DummyAtCallListCalls : CallListCalls
448{
449    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
450    {
451        //calls = cmd.calls;
452    }
453}
454
455public class DummyAtCallSendDtmf : CallSendDtmf
456{
457    public override async void run( string tones ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
458    {
459    }
460}
461
462public class DummyAtCallRelease : CallRelease
463{
464    public override async void run( int id ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
465    {
466    }
467}
468
469public class DummyAtCallReleaseAll : CallReleaseAll
470{
471    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
472    {
473    }
474}
475
476public class DummyAtPdpActivateContext : PdpActivateContext
477{
478    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
479    {
480        var data = theModem.data();
481        if ( data.contextParams == null )
482        {
483            throw new FreeSmartphone.Error.INVALID_PARAMETER( "No credentials set. Call org.freesmartphone.GSM.PDP.SetCredentials first." );
484        }
485    }
486}
487
488public class DummyAtPdpDeactivateContext : PdpDeactivateContext
489{
490    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
491    {
492    }
493}
494
495public class DummyAtPdpGetCredentials : PdpGetCredentials
496{
497    public override async void run() throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
498    {
499        var data = theModem.data();
500        if ( data.contextParams == null )
501        {
502            apn = "";
503            username = "";
504            password = "";
505        }
506        else
507        {
508            apn = data.contextParams.apn;
509            username = data.contextParams.username;
510            password = data.contextParams.password;
511        }
512    }
513}
514
515public class DummyAtPdpSetCredentials : PdpSetCredentials
516{
517    public override async void run( string apn, string username, string password ) throws FreeSmartphone.GSM.Error, FreeSmartphone.Error
518    {
519        var data = theModem.data();
520        data.contextParams = new ContextParams( apn, username, password );
521    }
522}
523
524/**
525 * Register all mediators
526 **/
527public void registerDummyMediators( HashMap<Type,Type> table )
528{
529    table[ typeof(DebugCommand) ]                 = typeof( DummyAtDebugCommand );
530    table[ typeof(DebugInjectResponse) ]          = typeof( DummyAtDebugInjectResponse );
531    table[ typeof(DebugPing) ]                    = typeof( DummyAtDebugPing );
532
533    table[ typeof(DeviceGetAlarmTime) ]           = typeof( DummyAtDeviceGetAlarmTime );
534    table[ typeof(DeviceGetCurrentTime) ]         = typeof( DummyAtDeviceGetCurrentTime );
535    table[ typeof(DeviceGetInformation) ]         = typeof( DummyAtDeviceGetInformation );
536    table[ typeof(DeviceGetFeatures) ]            = typeof( DummyAtDeviceGetFeatures );
537    table[ typeof(DeviceGetFunctionality) ]       = typeof( DummyAtDeviceGetFunctionality );
538    table[ typeof(DeviceGetMicrophoneMuted) ]     = typeof( DummyAtDeviceGetMicrophoneMuted );
539    table[ typeof(DeviceGetPowerStatus) ]         = typeof( DummyAtDeviceGetPowerStatus );
540    table[ typeof(DeviceGetSpeakerVolume) ]       = typeof( DummyAtDeviceGetSpeakerVolume );
541    table[ typeof(DeviceSetAlarmTime) ]           = typeof( DummyAtDeviceSetAlarmTime );
542    table[ typeof(DeviceSetCurrentTime) ]         = typeof( DummyAtDeviceSetCurrentTime );
543    table[ typeof(DeviceSetFunctionality) ]       = typeof( DummyAtDeviceSetFunctionality );
544    table[ typeof(DeviceSetMicrophoneMuted) ]     = typeof( DummyAtDeviceSetMicrophoneMuted );
545    table[ typeof(DeviceSetSpeakerVolume) ]       = typeof( DummyAtDeviceSetSpeakerVolume );
546
547    table[ typeof(SimChangeAuthCode) ]            = typeof( DummyAtSimChangeAuthCode );
548    table[ typeof(SimDeleteEntry) ]               = typeof( DummyAtSimDeleteEntry );
549    table[ typeof(SimDeleteMessage) ]             = typeof( DummyAtSimDeleteMessage );
550    table[ typeof(SimGetAuthCodeRequired) ]       = typeof( DummyAtSimGetAuthCodeRequired );
551    table[ typeof(SimGetAuthStatus) ]             = typeof( DummyAtSimGetAuthStatus );
552    table[ typeof(SimGetServiceCenterNumber) ]    = typeof( DummyAtSimGetServiceCenterNumber );
553    table[ typeof(SimGetInformation) ]            = typeof( DummyAtSimGetInformation );
554    table[ typeof(SimRetrieveMessage) ]           = typeof( DummyAtSimRetrieveMessage );
555    table[ typeof(SimRetrievePhonebook) ]         = typeof( DummyAtSimRetrievePhonebook );
556    table[ typeof(SimSetAuthCodeRequired) ]       = typeof( DummyAtSimSetAuthCodeRequired );
557    table[ typeof(SimSendAuthCode) ]              = typeof( DummyAtSimSendAuthCode );
558    table[ typeof(SimSetServiceCenterNumber) ]    = typeof( DummyAtSimSetServiceCenterNumber );
559    table[ typeof(SimUnlock) ]                    = typeof( DummyAtSimUnlock );
560
561    table[ typeof(SmsRetrieveTextMessages) ]      = typeof( DummyAtSmsRetrieveTextMessages );
562    table[ typeof(SmsSendTextMessage) ]           = typeof( DummyAtSmsSendTextMessage );
563
564    table[ typeof(NetworkGetSignalStrength) ]     = typeof( DummyAtNetworkGetSignalStrength );
565    table[ typeof(NetworkGetStatus) ]             = typeof( DummyAtNetworkGetStatus );
566    table[ typeof(NetworkListProviders) ]         = typeof( DummyAtNetworkListProviders );
567    table[ typeof(NetworkRegister) ]              = typeof( DummyAtNetworkRegister );
568
569    table[ typeof(CallActivate) ]                 = typeof( DummyAtCallActivate );
570    table[ typeof(CallHoldActive) ]               = typeof( DummyAtCallHoldActive );
571    table[ typeof(CallInitiate) ]                 = typeof( DummyAtCallInitiate );
572    table[ typeof(CallListCalls) ]                = typeof( DummyAtCallListCalls );
573    table[ typeof(CallReleaseAll) ]               = typeof( DummyAtCallReleaseAll );
574    table[ typeof(CallRelease) ]                  = typeof( DummyAtCallRelease );
575    table[ typeof(CallSendDtmf) ]                 = typeof( DummyAtCallSendDtmf );
576
577#if 0
578    table[ typeof(PdpActivateContext) ]           = typeof( DummyAtPdpActivateContext );
579    table[ typeof(PdpDeactivateContext) ]         = typeof( DummyAtPdpDeactivateContext );
580#endif
581    table[ typeof(PdpSetCredentials) ]            = typeof( DummyAtPdpSetCredentials );
582    table[ typeof(PdpGetCredentials) ]            = typeof( DummyAtPdpGetCredentials );
583
584}
585
586} // namespace FsoGsm
587
588// vim:ts=4:sw=4:expandtab
Note: See TracBrowser for help on using the repository browser.