Objecktorienterad utveckling i ANSI C – del 1

22 juli, 2010

Även om jag en gång i tiden började med, och länge utvecklade med, procedurella språk så har jag genom åren börjat tänka mycket mera objekt orienterat när jag utvecklar. Det började när man lärde sig C++ och har blivit ett intuitivt sätt för mig att se lösningar på mina utvecklingsproblem. För ett tag sedan fann jag mig dock i situationen att vara begränsad till ANSI C. Koden skulle kompileras på gcc och således är new, class och liknande bara att glömma. Mycket av koden på plats var skriven rent procedurellt, men den del försökte kapsla in olika delar genom att gruppera dem i olika filer osv. Jag fann det överraskande svårt att helt släppa det objektorienterade tänket och började istället att titta på vad jag skulle kunna göra för att iaf få in en del av de saker jag tycker om med objekt orienteringen i min kodning även i ren ANSI C. Detta är första gången jag gör detta, så jag lär mig och kommer på nya saker hela tiden och mycket kommer nog revideras. Men här följer lite tankar.

Arv
Arv kan till viss del fejkas genom att dina strukar ser identiska ut på de delar som de har gemensamt och att alla specifika delar kommer sist i structen.

struct foo {
  int a;
  int b;
}

struct bar {
  int a;
  int b;
  char* s;
}

void printStructstruct foo* obj) {
  fprintf(stderr, ”a: %s\nb: %s\n”, obj->a, obj->b);
  return;
}

printStruct kommer nu kunna ta emot både foo och bar structar. Men nackdelen är att du själv måste typkasta din bar struct om du vill skicka in den till funktionen. Funktionen kan inte heller skriva ut strängen i en bar struct då det skulle bli problem om du i själva verket hade skickat en foo struct. Ett sätt att komma runt detta är att låta funktionen ta en void* istället, men då kan du ju verkligen skicka in vad som helst och du kan omöjligt veta vilken struct du verkligen tar emot. En ”lösning” kan vara att ha ett typ fält i din struct. Du kan sedan låta funktionen kolla vilken typ det är och välja vad den skall göra på structen utifrån det.

enum StructType {FOO, BAR};

struct foo {
  structType type;
  int a;
  int b;
}

struct bar {
  structType type;
  int a;
  int b;
  char* s;
}

void printStruct(void* obj) {

  // Typkasta till den ”lägsta” typen och kolla typefältet
  switch( ((foo*)obj)->type ) {
  case FOO:
    fprintf(stderr, ”a: %s\nb: %s\n”, ((foo*)obj)->a, ((foo*)obj)->b);
    break;
  case BAR:
    fprintf(stderr, ”a: %s\nb: %s\ns: %s\n”, ((foo*)obj)->a, ((foo*)obj)->b, ((foo*)obj)->s);
    break;
  default:
    fprintf(stderr, ”UNKOWN TYPE!\n”);
    break;
  }

  return;
}

Nu kommer funktionen att skriva ut strängen om det är en bar struct du skickar in. Självklart måste type sättas till rätt värde, något som jag sköter via new funktioner som skapar mna structar. Till exempel

struct foo* MyFoo = NewFoo(aVal, bval);

Jag har hört rykten om att man skall kunna definera sin struct på ett sätt så att man faktiskt skulle kunna låta funktionen ta en pekare till ”bas” structen, men detta är inget jag lyckats med. Om någon vet så hojta gärna till.

Privata variabler
Att ha en massa variabler i våra objekt som vi egentligen inte vill att änvändaren skall pilla på har vi alla varit med om. I C++ deklarerar vi dessa som private. Men i en struct i ANSI C så finns inte denna möjligheten. Vad du dock kan göra är att i din .h fil skriva

typedef struct MyObj MyObj;

Och i din .c fil skriver du sedan

struct MyObj {
  int a;
  int b;
}

Nu kommer alla dina funktioner i din .c fil för objektet känna till hur din struct ser ut inuti, men för alla andra som bara har deklarationen i din .h fil att gå på så kommer medlemsvariablerna att vara gömda. Vi har nu lyckats återskapa lite av möjligheterna med privata variabler. Om du vill ha en statisk privat medlemsvariabler så åstadkommer du liknande funktionalitet genom att du deklarerar den utanför din struct i din.c fil.

Medlemsfunktioner
Medlemsfunktion är trevligt att ha om du tex har en array med pekare till dina structer som alla nu kan vara av lite olika typ. Men vi vet att alla skall köras genom en run funktion. Dock skall inte alla behandlas lika. Här kan vi göra som innan att kolla en type variabel i vår struct och utifrån den välja vad vi skall göra. Något som dock är snyggare är att använda medlemsfunktioner. Något som faktiskt även det går att uppnå med ANSI C.

typedef int funcPtr();
typedef struct myObj {
  int a;
  int b;
  funcPtr* run;
} myObj;

typedef struct myOtherObj {
  int a;
  int b;
  funcPtr* run;
  char* s;
} myOtherObj;

int runOne() {
  return 1;
}

int runTwo() {
  return 2;
}

I dina new funktioner för de olika ”objekten” (som jag nu hellre vill börja kalla dem än structar… hehe) så tilldelar du funktionerna.

struct MyObj* NewMyObj(int aVal, int bVal) {
  struct MyObj* this = (stuct MyObj*)malloc(sizeof(struct MyObj));
  this->a = aVal;
  this->b = bVal;
  this->run = runOne;
  return this;
}

Sedan kan du i din main göra

struct MyObj* myObj = NewMyObj(10, 20);
int result = myObj->run();

Variabeln result kommer nu vara 1 då vi tilldelade runOne till vår run-funktion i konstruktorn och den funktionen returnerar 1. Om detta objekt var en del av en array av objekt hade vi kunnat loopa dem och köra deras respektive run-funktioner och var och en av objekten kunde i sin tur peka på helt egna implementationer av den funktionen.

Detta var bara lite snabba saker som jag kommit på under de senaste dagarna. Kom gärna med tipps och idéer. Och om ni finner detta intressant och vill ha mera av denna varan om jag kommer på mera saker så hojta till. Koden ovan är inte testkompilerad så kan vara några småfel här och där, men själva idén bör framgå.

Dela

Lägg till border runt bild i PHP

11 juni, 2010

Fick en fråga häromdagen om hur man kan skapa en border runt en bild i PHP. Slängde ihop en liten kodsnutt som som jag tänkte dela med mig av här.

  |  copy code |? 
01
02
/*
03
* Function to create a border around an image
04
*/
05
function drawBorder($image_name, $r = 0, $g = 0, $b = 0, $thickness = 1)
06
{
07
  $image = ImageCreateFromJPEG($image_name);
08
  $color = ImageColorAllocate($img, $r, $g, $b);
09
 
10
  $x1 = 0;
11
  $y1 = 0;
12
  $x2 = ImageSX($image) - 1;
13
  $y2 = ImageSY($image) - 1;
14
 
15
  for($i = 0; $i < $thickness; $i++)
16
  {
17
    ImageRectangle($image, $x1++, $y1++, $x2--, $y2--, $color);
18
  }
19
 
20
  return $image;
21
}

Sedan kan du köra något i stil med

  |  copy code |? 
1
header('Content-type: image/jpeg');
2
ImageJPEG(drawBorder("images/foo.jpg", 128, 128, 0, 3));

Bör förmodligen lägga till en switch-sats i funktionen som faktiskt kollar vilken typ av bild du använder dig av och använder sig av rätt ImageCreateFrom* funktion på bilden i fråga och lite exception handling. Men här har ni en grund.

Dela

Frilansare – En handelsvara eller ett jobb?

10 juni, 2010

Idag är det många som extraknäcker som frilansare eller som som sin huvudsakliga sysselsättning jobbar som självständig konsult eller frilansare. Det faktum att det är många som i olika omfattning jobbar som frilansare bidrar till att det finns ett brett spektrum av människor som är aktiva. Allt ifrån unga studenter som vill dryga ut kassan lite till erfarna utvecklare som jobbat på och med kritiska applikationer på professionell nivå i flera år. Mycket av resonemanget här passar in på frilansare i många olika branscher, men jag hämtar mina erfarenheter mestadels från IT branschen.

Ett populärt ställe för dessa personer att söka uppdrag är på diverse siter inriktade på att förmedla kontakter mellan frilansare och projektbeställare. Flertalet av dessa fungerar som så att en projektbeställare lägger upp en beskrivning av projektet och frilansare sedan ”budar”, d.v.s. presenterar sina priser för att lösa projektet. Ett annat populärt upplägg är när projektbeställaren på förväg anger en budget och de frilansare som vill ta sig an jobbet måste rätta sig efter detta. Något som dock blivit uppenbart när jag själv varit i kontakt med denna typ av siter är prisnivåerna som ofta är förvånansvärt låga. Även om en projektbeställare ber om en komplett webbaserad lagerlösning som i runda slängar hade tagit två manmånader att utveckla så ligger kanske budgeten för projektet på 20.000-40.000kr, ibland ännu lägre. Samma tidåtgång från en större konsultfirma hade haft en nota på runt 250.000-300.000kr. Då skulle man kunna tänka att alla frilansare hade rynkat på näsan och undrat i vilken verklighet projektbeställaren levde och låta denna själv få förstå detta då ingen visade intresse i projektet. Men så är inte fallet, utan mot allt sunt förnuft så är det ofta massor med bud om att göra projekten för dessa låga ersättningar och ibland ännu lägre. Varför skulle någon vilja gå ännu lägre då? Jo, för att få projektet så klart. Samma sak återfinns även i enklare projekt där en projektbeställare kan be om en komplett Joomla site designat och utvecklad på en vecka för 1000kr. Frågorna som då blir intressanta att besvara är

  • Varför är priserna så låga?
  • Tjänar beställarna på att priserna är så låga?
  • Gynnas marknaden som helhet av det nu rådande läget?

Varför anser jag att priserna är låga?

Låt oss utgå ifrån ett exempel ifrån verkligheten:
En projektbeställare vill ha en site till sitt nystartade företag. Siten skall designas och kodas för 5000kr. Siten skall bestå av en framsida och fyra undersidor.

Om vi utgår ifrån att en person sköter både design och kodning för att få det så billigt som möjligt så är ett rätt normalt tillvägagångssätt att man gör en design ide som kunden får se. Detta kan ske på papper eller som en bildfil i datorn. Om kunden vill ha ändringar så iterarar man här tills kunden är nöjd. När kunden godkänner designen så för man över den till HTML och CSS. Efter att detta är klart så börjar man koda funktionalitet i ett språk som ASP, PHP, ASP.Net och kanske en del scriptning i Javascript.

Frilansaren jobbar under förmiddagen ihop ett förslag som skickas till kunden. Kunden har några få ändringar som denne vill införa och en reviderad version skickas på eftermiddagen till kunden. Designen godkänns och dag två påbörjas jobbet med att föra över designen till HTML och CSS. Arbetet är klart strax innan lunch och kodningen av funktionaliteten påbörjas under eftermiddagen dag två. Det är inga avancerade saker som skall kodas utan bara kontaktformulär och ett enklare administrationssystem för kunden att lägga upp nyheter och bilder, kodingen av detta är avklarat vid slutet av dag tre. Dag fyra sker installation på kundens webhotel och kunden informeras om hur allt fungerar etc. Denna enkla uppgift har då tagit iaf fyra mandagar och då räknar vi med att kunden varit så precis med sin design idé att denne bara behövde nämna en mindre ändring och inte kommit på några nya ideér under resans gång. Sanningen bakom denna typ av ”små” projekt är dock ofta det motsatta. Kunden kommer på designen allt eftersom och funktioner växer fram i takt med att projektet fortlöper. Men, om vi ändå tänker oss att detta projekt tog fyra mandagar så har vi en timpenning på ca 156kr, inklusive moms då kunden satt sitt totala tak på 5000kr.

Om vi räknar på denna timpenning och full beläggning med en månad ledighet under året hamnar vi på en årsvinst på 206.000kr (ex moms då detta inte påverkar din vinst) för frilansaren. Då skall man veta att hundra procent debiterbar tid är väldigt, väldigt sällsynt.

Efter skatter, egenavgifter osv hamnar vi på en årslön 122.000kr. Detta ger en månadslön, efter skatt, på 10.166kr vilket motsvarar en lön på runt 13-14.000 om man istället var anställd. Då en någorlunda erfaren webbutvecklare kan ligga runt 25-30.000kr i månadslön och systemutvecklare i andra språk ännu högre så ser man snabbt att ersättning för jobbet är på tok för liten. Vad skedde då med detta projekt? Skrattade folk åt ersättningsnivån och önskade projektbeställaren lycka till att hitta någon som ville ta sig an projektet? Nej, istället var det uppemot 70 frilansare som slogs om uppdraget. Projektet tog ju sedan naturligtvis längre tid än de estimerade fyra dagarna samt att kunden kom på ny funktionalitet under resans gång. Slutresultatet var att ersättningen för arbetet blev ännu lägre än i exemplet ovan. Det är nog rätt uppenbart för alla att detta är en orimlig ersättningsnivå för det utförda arbetet, men hur kommer det sig då att det gång på gång sker arbeten till sådana kraftiga underpriser? Varför gör frilansare med på det och tjänar projektbeställarna verkligen något på detta?

De låga priserna kan förmodligen förklaras på flera sätt. Först har vi det faktum att vi alla när vi begär in offerter på jobb i hemmet osv jagar det billigaste priset (något som inte heller alltid är det bästa alternativet som en populär tv serie tydligt har visat). Lägg då till det faktum jag nämnde innan, att frilansarna består av allt från extraknäckande studenter till professionella individer som jobbat flera år med utveckling, kanske har familj, hus och tänker på saker som sitt pensionssparande. Vem av dessa två kan erbjuda det lägsta priset? Studenten vinner nog den fajten. Studenten kan ha råd att jobba till underpris då deras utgifter ofta inte är i närheten av de som en arbetande person med familj och hus. Det lilla extra studenten tjänar på sitt frilansande är ju ändå ett stort plus i kassan för denne. Men de långsiktiga effekterna kan bli att marknaden acklimatiserar sig till dessa underpriser. Studenten kommer skjuta sig själv i foten då denna lite senare i livet själv kommer märka att ersättningen inte går att leva på. Då är det lätt att tänka att man lätt kan få mera ersättning då man nu har mera erfarenhet, men precis som för alla som idag försöker sälja sina tjänster så kommer man märka att det finns nya studenter med låga priser att matcha.

Man skulle kunna tänka sig att erfarenheten hos seniora utvecklare väger tungt hos projektbeställarna. Men om det står mellan två personer som båda lovar att de löser uppgiften och den ena säger sig göra det för 250kr/timme och den andre vill ha 800kr/timme så väljer nästan alla den som tar 250kr/timme. Något som dock visat sig med tiden är att många som nappar på ”bästa priset” i slutändan får betala mycket dyrt för sitt val. Självklart finns det utvecklare som jobbar till underpris, men ändå gör ett bra jobb. Men väldigt många kunder vittnar om personer som pressat sina priser för att få uppdraget. När det väl skett så har själva samarbetet inte alls förflutit så smärtfritt som kunden först fått bilden av att det skulle göra. Kommunikationen är ofta bristfällig, leveransdatum skjuts hela tiden framåt, folk hoppar av projekten mitt i och en del lösningar är helt enkelt bara tekniskt ogenomtänkta, något som ofta beror på bristande arbetslivserfarenhet hos den som utför projektet. En mera erfaren utvecklare som pressar sitt pris för att matcha alla de låga priserna på marknaden kan också lätt få problem. Man måste planera tiden precist för att hinna pressa in så mycket jobb som möjligt för att få in tillräckligt med pengar så att det går runt. Problematiken blir då att det blir väldigt svårt för projektet att vara dynamiskt och växa med tiden då utvecklaren med stor sannolikhet måste ha parallella projekt löpande.

Vad blir då resultatet av detta i längden, tjänar någon egentligen på att en marknad acklimatiserar sig till en prisnivå som är ohållbar? Hur reagerar marknaden när vändningen kommer och frilansare inser att de måste börja ta betalt för sina tjänster, och varför finner sig frilansare i att vara en handelsvara? Kommer frilansmarknaden att tömmas på kompetent folk då de hellre tar anställningar för vettiga löner?


Dela

Merge branch med trunk med Subversion och Eclipse

10 juni, 2010

Många har idag insett fördelarna med någon form av versionhantering när de utvecklar. Vare sig du använder dig av CVS, SVN, git, perforce eller något annat system så finns det mycket att tjäna på denna typ av system. Detta blir speciellt uppenbart när många personer jobbar på ett gemensamt projekt och flera olika team kanske jobbar på olika features parallellt. Här kommer vi att tala om Subversion (SVN) och konceptet branches (grenar) som existerar i de flesta versionhanteringssystem. Var branches gör är att de låter dig hantera olika versioner av din kodbas parallellt för saker som att underhålla gamla versioner, utveckling av nya features eller experimentella features som kanske aldrig blir en del av produkten. Men med branching så kommer också något som kallas merging, alltså att slå ihop ändringarna i din parallella kodbas med den ursprungliga igen. Detta görs mer eller mindre bra i olika versionhanteringssystem. Här skall jag visa hur du går tillväga om du använder SVN via Subclipse under Eclipse.

För en som aldrig gjort detta tidigare så kan det vara lite av en djungel, speciellt när man gör det från terminalen. Fram tills nydligen hade inte SVN något direkt bra stöd för det heller om man skötte ofta mergning med ett externt python script. Nu klarar dock SVN av det hela och med Subclipse kan det nästan bli riktigt enkelt även för någon som aldrig tidigare gjort en merge. Stegen är i princip identiska med de hos alla GUI’n för SVN eller om man gör det direkt i terminalen.

Merge Branch till Trunk

Låt oss säga att du haft en sprint (om ni tex kör SCRUM) eller bara är klar med en ny feature som ni utvecklat i en egen branch för att inte störa kodbasen i trunken (som ofta är bra att hålla i ett skick där det går att kompilerbar och släppa). Vad vi nu vill göra är alltså att föra in våra ändringar ifrån vår branch tillbaka till vår trunk. Vad vi nu skall göra, steg för steg, är följande.

  1. Se till att all kod i trunken är uppdaterad (vilket den normalt skall vara om folk inte arbetar mot trunken i sin dagliga utveckling, något jag avråder ifrån).
  2. Lös alla konflikter.
  3. Se till att din working copy nu är trunk, dvs ditt du skall merga till. Här blir många ofta konfunderade och tänker att de måste ju stå i den branch de vill merga in till trunk. Men tänk som så att du skall stå där dit du vill lägga in ändringarna som gjorts i någon annan branch.
  4. Välj Merge ifrån Team menyn på trunk.
  5. Som From URL pekar du på den branch du vill merga ihop med din trunk. I detta fall branch 2.65 av bar.
  6. Ändra From Revision till den senaste revisionen som mergades ihop med din trunk. Detta är då man generellt inte vill hålla på att merga hela branchhistoriken. Du vill bara köra merge på de ändringar som skett sedan du sist gjorde en merge.Jag vet inte om SVN nu har något bra system för att hålla koll på detta själv, men förut (och jag fortfarande) letar upp detta revisionsnummer genom att ditta på mina logg över commit meddelanden. Håll även detta revisionsnummer i minnet då vi kommer använda det i vårt commit message för mergen senare. I många fall, speciellt om man inte mergat fram och tillbaka som en galning, kan man komma undan med att köra HEAD:HEAD när man gör sin merge.
  7. To Revision skall oftast vara HEAD då man vill merga till och med sin sista commit.
  8. Klicka nu på Merge och låt SVN göra sitt jobb.
  9. Lös alla uppkomma konflikter.
  10. Gör en commit som inkluderar hela din merge. Ett exempel på commit message för en merge kan vara
    Merging [source] to [target]; [repository]. Merge rev [start]:[end]

Hoppas denna lilla snabba guide kan hjälpa några av er.

Dela

Spray hackat igen!

2 juni, 2010

Spray, en av Sveriges största portaler och communitys på internet, har hackat… för andra gången!

– Obehöriga kunde under en kort stund komma åt våra användares lösenord, säger Fredrik Pallin, presschef på företaget.

Många var vi som rynkade på ögonbrynen när Spray hackades för ett antal månader sedan. Via ett säkerhetshål hade anfallare kunnat ladda upp ett script som gjorde det möjligt att söka ut information om alla användare på Spray. Då sparades lösenorden i klartext och Spray fick stå med svansen mellan benen och skämmas lite över vad som till synes såg ut som dålig hantering av säkerheten för användarna. Det var inte första gången Spray hackades och man lovade bättring. Därför är jag än mer förvånad och i det närmaste förfärad över den till synes låga kvalitén på koden hos Spray när ytterligare ett säkerhetshål rapporterades. Denna gång har Spray iofs hashat lösenorden med SHA1, men då man får ut all information om användaren och dennes säkerhetsfråga i klartext vilket gör det rätt enkelt att ta över kontot.

Information i klartext

Gruppen ”Vuxna Förbannade Hackare” har tagit på sig attacken och det är inte första gången Spray blir överbemästrade av denna grupp.
Säkerhetshålet diskuteras på Flashback, låt oss hoppas att Spray läser på lite om it-säkerhet och systemutveckling nu.

Dela

Vinn en Nexus One

23 februari, 2010


City Network har just nu en tävling där man kan vinna en Nexus One. Många är vi säkert som längtat efter denna telefon och nu när den äntligen är här så skadar det ju inte att få en för 0 kr. För egen räkning hade jag gärna haft en för att utveckla applikationer till och i dagsläget så tror jag att mina våta drömmar om iPhone får ge vika för suktandet efter en Nexus One.

City Network är ett webbhotell beläget i Karlskrona. Med över 7000 kunder runtom i hela Sverige är City Network en av de absolut ledande webbhotellen. De driftar allt från enklare hemsidor till stora system om hundratals servrar där de som experter hjälper med redundans, säkerhet och prestanda. På en tidigare arbetsplats var vi kunder hos City Network med ett mycket stort antal servrar och jag kan verkligen rekommendera deras tjänster.

“I love it” – Säger Johan som testat Nexus One för City Networks räkning


Dela

Korrupt .NET 2.0 förhindrar anslutning till WHS

1 september, 2009

Jag talade i en tidigare post om problem med att koppla upp till sin WHS via WHS Console direkt efter en installation. Något som då berodde på ”felaktigt” användarnamn på datorn som försöker ansluta. Det finns ytterliggare ett tillfälle då man kan få problem att koppla upp sig mot sin WHS. Då detta skett både på min egen Scaleo och en kunds Scaleo en gång så känns det som något som kanske inte är helt ovanligt. Om det är något som drabbar just Scaleo eller om det är något i operativsystemet vet jag dock inte. För mig skedde det hela strax efter jag bytte processor. Då min kund hade haft problemet och jag fixade det åt honom så var jag förberedd på att det kanske skulle bli problem så fixade jag det i samma veva. Men när det skedde min kund så var det lite mera av ett mysterium.

Symptom

Min kund förklarade att han kunde ansluta till sina shared folders utan problem. Men han kunde inte ansluta via WHS Console. När jag bad honom testa ansluta via RDC så svarade han att inte heller detta fungerade. Symptomen till problemet är alltså att du kommer åt alla delade filer som vanligt, men inte kan ansluta via varken WHS Console eller RDC.

Undersökning

Jag kopplade upp servern till ett grafikkort via adapter som jag talat om i posten om processorbyte på Scaleo 2205. Har ni en DYI WHS så har ni säkert möjligheten att ansluta en monitor till den, annars måste ni undersöka hur ni skall göra på just er modell. Väl inne i Windows så inspekterade jag Event loggen, den finns under ”Administrative Tools”. Jag försökte starta WHS Console lokalt på servern samtidigt som jag höll koll på loggen. Jag noterade en krash av WHS Console som berodde på något fel i .NET 2.0. Det visar sig snart att .NET 2.0 på servern blivit korrupt, något som också påverkar RDC då den verkar använda sig av .NET 2.0. Detta leder således till att man varken kan ansluta med WHS Console eller via RDC.

Lösningen

Jag försökte avinstallera eller installera över en ny version av .NET 2.0, men vad jag än testade så gick det helt enkelt inte. Det finns sätt att ”tvinga” ett MSI paket att installera sig, men inget sådant hjälpte heller. Då Windows sedan Windows XP (kanske till och med tidigare) sparar kopior på många systemfiler för att just kunna ersätta korrupta versioner så tänkte jag att jag ger det ett försök. För att tvinga fram en scan av systemfilerna så öppnar man upp kommandotolken (är lite unix/osx skadad och vill säga terminalen). Detta kan görav genom att välja kör/run frön startmenyn och skriva cmd  kör följande kommando sfc /scannow. Detta startar en scan av systemfilerna som kan ta ett tag. När det är klart får du starta om servern och hålla tummarna att det löste ditt problem. Detta fungerade på både min egen och min kunds server.

Avslutning

Då det skedde väldigt nära inpå för oss båda så funderade jag en del över om det kanske var någon update till systemet släppt av Microsoft som i olyckliga fall kunde resultera i detta. Jag har inte undersökt det närmare, men om någon vet anledningen bakom det hela så får ni gärna dela med er.

Dela

Time machine backup till Windows Home Server

31 augusti, 2009

När jag disskuterat Windows Home Server (WHS) med folk så får jag ofta frågan om den kommer funka med Time Machine på deras Macbook eller liknande. Sedan Apple dödade smb stöd i Time Machine så är det inte riktigt lika lätt som det var förut längre. HP Media Smart Server har en add-in ger stöd för Time Machine, men hur gör alla vi andra som sitter på en WHS av annat märke, eller rent av ett hemmabygge? Jo, det går att lösa även för oss, man får bara anstänga sig lite mera. Lärde mig de följande stegen från Brent på Compugeek Software.

Gör din WHS redo för Time Machine

  • Skapa en ny shared folder för Time Machine på din WHS. 
  • Om du känner att du inte vill ansluta till din WHS via Remote Desktop Connection (RDC) så får du även installera Advanced Admin Console Add-In (AAC) på din WHS.

Gör din Mac redo för Time Machine till WHS

  • Starta upp en Terminal och kör defaults write com.apple.systempreferences TMShowUnsupportedNetworkVolumes 1.
Aktivera Time Machine till shares

Aktivera Time Machine till shares

Konfigurera Time Machine

Efter att du aktiverat möjligheten till att använda shared folders som Time Machine backup destinationer så måste du mounta den folder som du tidigare skapade för ändamålet i WHS.

  • Öppna Finder och lokalisera din WHS
  • Dubbelklicka på din WHS och om du får en förfrågan om användarnamn och lösenord så fyller du i uppgifterna för en användare på din WHS som har access till den shared folder du tidigare skapade. Om du inte blir efterfrågad efter användarnamn och lösenord så klickar du på ”Connect as”. Uppgifterna måste du välja att spara i keychain, annars får Time Machine problem när den skall göra sina backups.
  • Navigera fram till foldern du skapade för Time Machine och öppna den. Du kan nu komma åt den i Time Machine Preferences.
  • Öppna Time Machine Preferences och välj din share. Den kommer mountas som en disk och automatiskt re-mountas när Time Machine behöver göra en backup. När du blir förfrågad om användarnamn och lösenord så använder du samma som innan.
  • Avaktivera backups

TimeMachine_DiskSelection_2

Den första backupen

Sedan Leopard 10.5.2 så har Apple ändrat hur Time Machine till en shared folder fungerar. Som ett resultat av detta så kommer Time Machine att misslyckas med att skapa den första backupen. För att komma runt detta gör du följande.

  • Anslut till din WHS via RDC eller starta upp WHS Console och gå till AAC som du i så fall installerade tidigare.
  • Gå till D:\shares\TIME_MACHINE_SHARE_NAME – TIME_MACHINE_SHARE_NAME skall vara namnet på den folder du skapade för backuperna.
  • På din Mac så aktiverar du backuper och initierar en backup.
  • Om du är snabb som kommer du se en ny fil i din WHS share. Den kommer vara gömd så du måste ha aktiverat att du vill se gömda filer.
  • Kopiera snabbt filen till D:\shares.

Skapa en fungerande sparsebundle fil

Eftersom Time Machine misslyckas med att skapa filen automatiskt så måste vi göra det manuellt.

  • Starta Applications/Utilities/Disk Utility på din Mac
  • Klicka på File->New->Blank Disk Image
  • Navigera till din desktop i ”where to save”
  • Som namn använder du namnet på den fil du kopierade till D:\shares, men ta bort .tmp.sparsebundle från namnet.
  • Som volume name väljer du Time Machine
  • Som image format väljer du sparse bundle disk image
  • Partitions skall vara No partition map
  • Volume format skall vara Mac OS Extended (Journaled)
  • Volume size, välj custom och sedan väljer du så många GB som du vill att Time Machine max skall få utnyttja på din WHS
  • Klicka på create
  • Ejecta den nya disk images från Finder
  • Flytta nu den nydligen skapade filen till din Time Machine Share på din WHS
  • Aktivera backup igen ifrån Time Machine Preferences och starta en backup. Den kommer nu att avsluta det förberedande stadiet och göra sin initiala backup.
  • Ta bort filen du kopierade till D:\shares

Avslutning

Så, nu bör din Mac kunna använda din WHS som destination för sin Time Machine Backup. Slå på folder duplicering om du vill ha lite extra skypp på din Time Machine backup. Jag väntar med spänning på att Microsoft skall inse hur många som idag sitter på både WIndows och OSX datorer och implementera stöd direkt i WHS så att man på samma sätt som för en Windows maskin kan välja mappar av vad som skall backupas osv från WHS Console.

Dela

Klickbar mailto länk utan att få skräpmail från spambots

28 augusti, 2009

Borta är dagarna då en publik hemsida utan att bry sig om otrevliga konsekvenser kunde ha en mailto länk som kontaktväg. Spambots som crawlar websiter, samlar in mailadresser och skickar spam till dessa har gjort att folk fått börja tänka på alternativa sätt att förmedla sin mailadress. En del ersätter möjligheten med ett kontaktformulär och kör captcha validering för att undvika att botar fyller i formuläret. Andra lägger helt enkelt in en bild med texten för mailadressen istället. Det finns flera lösningar på ”problemet” men det alla dessa har gemensamt är att de försvårar. Inte bara för en spambot utan även för vanliga användare som faktiskt vill komma i kontakt med ert företag eller dig som person. Antingen måste de spendera tid med att fylla i ett formulär och försöka tyda en captcha som oftast är allt annat än lätt tolkad. Eller så måste de skriva av din mailadress ifrån en bild. Båda dessa försvårar och drar ner helhetsintrycket av användarens besök på din site. Tänk om man kunde ha en klickbar mailto länk utan att vara rädd för att spambotarna snor den och skickar skräpmail i sådana mängder att den enklaste lösningen till sist blir att byta företagsnamn och domänadress.

Självklart finns det sådana möjligheter. För att inte kräva något extra från användaren så brukar jag själv använda mig av javascript för att klara av detta. Det är faktiskt extremt enkelt och gör att länken utåt mot användaren fungerar precis som vanligt. Men en bot har mycket svårare att tyda den. Observera att OM boten vet vad den letar efter så kan de crawla även detta.För att göra det så jobbigt som möjligt så gör man därför klokt i att inte lägga koden direkt i länken som i exemplet nedan utan i en funktion i en javascript fil. Sedan förhindrar man direkt åtkomst till dessa via sin .htaccess fil så att folk inte direkt kan ladda den och titta efter lösningen där.

För att få en ”säker” mailto länk the quick and dirty way så gör du följande

  |  copy code |? 
1
<a href="#" onclick="var username='foo'; var domain='bar'; var countrycode='se'; this.href='m'+'a'+'i'+'l'+'t'+'o:'+username+'@'+domain+'.'+countrycode;">Skicka mail genom att klicka här!</a>

Istället för ”Skicka mail genom att klicka här!” kan man självklart ha sin mailadress, men då blir det lite för lätt att plocka ut den. Via detta kommer man iaf inte att kunna göra på det ”enkla” sättet, vilket ofta är att crawla en site för just ”mailto:” och extrahera mailadressen därefter. Skulle man göra detta här skulle man inte få ut något som gick att använda för att skicka spm till dig iaf.

Om man, som jag nämde innan vill vara ännu säkrare så gör man följande

  |  copy code |? 
1
// Function placed in js file
2
function getFooMailLink(linkelement) {
3
  var username = 'foo';
4
  var domain = 'bar';
5
  var countrycode = 'se'
6
 
7
  linkelement.href='m'+'a'+'i'+'l'+'t'+'o:'+username+'@'+domain+'.'+contrycode;
8
}

På html sidan skriver du sedan bara

  |  copy code |? 
1
<a href="#" onclick="getFooMailLink(this)">Skicka mail genom att klicka här!</a>

Nu kommer enkelheten hos mailto länkarna att finnas till hands för era besökare men ni slipper de jobbiga ”överraskningarna” som är associerade med att använda dem. Det finns säkerligen en hel del andra lösningar också på detta så dela gärna med er.

Dela

Länka anrop i PHP

27 augusti, 2009

När man kodar objektorienterat i PHP så använder man sig som i så många andra språk ofta av befintliga och beprövade design patterns. Dessa är generella återanvändbara lösningar på vanligt förekommande problem i systemutveckling. Till dessa hör bland annat Factory, Singelton, Adapter, Bridge, Iterator osv. Ett design pattern som jag själv aldrig kommit i kontakt med under mina år med C++ och PHP fångade min uppmärksamhet då det används flitigt i ZendFramework som jag använder flitigt. Jag är inte säker på att namnet jag använder är rätt, men jag kallar det Command Chaining. Om någon vet det riktiga namnet så lämna det gärna i komentarerna.

Det hela är egentligen väldigt enkelt. Genom att varje medlemsfunktion i klassen returnerar en $this referens (dvs en referens till instansen av klassen) så kan man på den kalla ytterliggare funktioner. Ett exeplel kunde vara

  |  copy code |? 
01
<?php
02
// Create a new instance
03
$ftp = new Ftp();
04
 
05
// Connect to the ftp, change to correct directory, 
06
// download a file and close the connection in one go
07
$ftp->connect('username', 'password', 'ftp.foobar.com')
08
    ->chdir('/folder_with_the_stuff')
09
    ->download('thefile.txt')
10
    ->closeConnection();
11
?>

Fördelen med detta design pattern är att man väldigt snabb och sammanhängande kan skriva en serie androp som hör ihop. Används t ex effektivt i ZendFrameworks Zend_Db_Table klass som fungerar som ett objektorienterat interface till databastabeller.

  |  copy code |? 
01
<?php
02
 
03
// Fetching a rowset
04
 
05
$rows = $table->fetchAll(
06
  $table->select()
07
        ->where('bug_status = ?', 'NEW')
08
        ->order('bug_id ASC')
09
        ->limit(10, 0)
10
    );
11
 
12
?>

En nackdel som jag dock tycker detta pattern har är att felhanteringen blir
mycket jobbigare. Eller nja, man måste tänka på att man gör det annorlunda
iaf. Säg att personen i exemplet med ftp klassen ovan kunde logga in men
 fanns inte just den filen han ville ladda ner, då kanske han vill ladda ner
en annan fil istället. Men hur gör man då? Om download() bara returnerar
en referens till $this ändå så kommer vi aldrig att få veta att filen inte fanns
och closeConnection() kommer sedan att köras. Att returnera en felkod
funkar inte heller eftersom closeConnection() då kommer försöka köras
 det returvärdet. Vad man bör och skall göra är naturligtvis att använda
Exceptions och kasta ett sådant i de funktioner där något går fel.

  |  copy code |? 
01
// Create a new instance
02
$ftp = new Ftp();
03
 
04
try {
05
// Connect to the ftp, change to correct directory, 
06
// download a file and close the connection in one go
07
$ftp-;>connect('username', 'password', 'ftp.foobar.com')
08
    ->chdir('/folder_with_the_stuff')
09
    ->download('thefile.txt')    
10
    ->closeConnection();
11
}
12
catch(Exception $e) {
13
// Handle the exception
14
...
15
?>

Om man är van vid att använda felkoder som returvärden som felhantering
så får man tänka om lite. Det ändrar också flödet med if() checkar man
kanske då traditionellt haft när man hanterat fel. Men kommar man bara
över det så kan detta design pattern snygga upp koden en hel del i ens
projekt. Om vill kan man börja lite enklare och tex implementera detta
på något så simpellt som en config klass. Om man låter alla
set−funktioner returnera en $this referens så kan man lätt känna lite
på detta design pattern utan att man behöver tänka så mycket på
felhanteringen som följer om funktionerna gör lite mera avancerade saker.

  |  copy code |? 
1
// Load config from inifile
2
$config = new Config('foobar.ini');
3
 
4
// Change some settings
5
$config->setWidth('200')->setHeight('400');
6
 
7
...
8
 
9
$config->save('foobar.ini');

Dela