Tag Archives: 해킹

농협 해킹사태가 북한 탓이라고? 한마디 안할수가 없다.

우선 이글을 썼다가 무슨일이 생길지 몰라 겁도 좀 나지만 이글은 정치적인 문제를 떠나 그냥 단지 리눅스 서버를 운영해온지 10년 넘은 사람으로써 순수히 엔지니어의 입장에서 써보는 글입니다. 이런 문제는 전문적인 이유를 설명해줘도 오해를 많이 살 법한 이야기인데 말도 안되는 이야기를 들고 나오면 더더욱 오해를 살수밖에 없을것 같습니다.

이 글을 쓰기에 앞서, 저는 보안 전문가가 아닙니다. 보안에 대하여 따로 공부하거나 학위를 가지고 있지도 않습니다. 단지 중학교때부터 리눅스를 사용해 왔고 고등학교때 부모님께 돈을 빌려 웹호스팅 사업을 해오면서 수많은 해킹의 피해를 입은 경험이 있는 사람일 뿐입니다.

[“농협 해킹은 北소행”] 검찰이 北지목한 이유-동아일보

동아일보의 오늘자 기사입니다. 검찰이 북한으로 지목한 이유가 3가지가 있습니다. 물론 저 역시도 북한일 수 있다는 가정을 처음부터 버리지는 않겠습니다. 북한 전체적인 해킹 실력이 비웃음을 살 지언정 해킹이라는것은 단 한명의 실력파만 있어도 충분히 일어나는 일이기 때문입니다. 사람수가 중요한건 아니라는거죠.

1. 北 3.4 디도스 공격때와 ’45자 암호키’ 똑같아

DDOS공격이란 Distributed of Denial Service의 약자로 서비스에 사용되는 서버가 혹은 네트워크가 버틸수 없을만큼의 서비스 요청등의 방법으로 서비스를 불가능하게 하는 해킹 기법입니다. 보통 웜/바이러스등을 이용하여 전세계의 불특정 다수의 사용자를 감염시켜 자신의 명령을 따르게 합니다.
이 바이러스에 감염이 되면 일명 좀비PC라는 애칭(?)을 가지게 되며 대부분의 경우가 공격자가 접속해 있는 IRC에 접속하여 그곳에서 명령을 받게 됩니다.
사용자 삽입 이미지좋은 그림이 있는것 같아 첨부해 봅니다. 명령을 내리게 되는 Commander는 IRC서버를 통해 좀비PC들에게 명령을 내리게 되며 공격 대상이 되는 Victim은 엄청난 공격을 받게 됩니다. 제가 운영하는 서버도 몇번 DDOS공격을 받은적이 있습니다.

처음에 잘 모를 시절에는 공격 들어오는 IP를 분석하여 iptables로 단순 차단하는 식으로 하였습니다. 공격의 대부분이 중국 IP였기 때문에 중국발 DDOS구나라고 단순히 생각하였습니다. geoip-extension을 이용하여 중국 전체의 IP를 차단했음에도 서비스가 복구가 되지 않았습니다.

다시 확인을 해보니 중국뿐만 아니라, 브라질, 프랑스, 미국, 한국 아예 전세계에서 공격이 들어오더군요. IP에 특정한 패턴이란건 존재하지 않습니다. 그리고 위의 그림에서 알 수 있듯이 DDOS의 공격에서는 어떤 의미를 가지고 있는 IP라는것은 없습니다. 공격 들어오는 IP는 99.99999% 순수하게 그 스스로들도 피해자인 좀비PC 또는 좀비서버의 IP일 뿐입니다.

자, 그럼 검찰에서 좀비PC를 통채로 가져가서 소스를 분석하여 얻을 수 있는 정보를 생각해 봅시다. 저는 개인적으로 얻을수 있는게 거의 없다고 보지만 그래도 그냥 추상적으로 컴퓨터를 잘 몰라도 위의 그림만을 보고 생각했을 때 명령을 받기 위해 IRC서버에 접속하기 위해 IRC서버의 주소는 알 수 있을것 같네요. 그리고 어쩌면 공격 대상의 IP도 남아있을지도 모르겠습니다. 웜바이러스는 메모리에 상주하며 명령을 전달 받을것입니다. 이 과정에서 공격대상의 IP는 따로 하드디스크에 저장할 필요는 없어보입니다. 하지만 있다고 좋게 가정해 보겠습니다. 그리고 어쩌면 어떤식으로 공격하는지 패턴도 알 수 있겠네요.

여기까지 알아냈다고 치고 이제 검찰이 그리고 국민이 알고 싶어하는 정보는 누가 공격했냐는 것이죠. 공격대상 IP는 필요가 없고 공격패턴같은것도 우선 제처두고 알아낼수 있는것은 유일하게 어떤 IRC로 접속했을까 하는 것입니다. 또는 IRC가 아니라도 공격자의 명령을 받기 위해 어딘가에 접속해야 한다는 것입니다.

암살 영화를 보면 엄청난 스킬을 가진 암살자는 다양한 방법으로 명령을 전달 받습니다. 심지어 의뢰자의 얼굴이나 목소리조차 모르기도 하죠. DDOS도 똑같습니다. 결과적으로 좀비PC는 직접적으로 명령을 전달받긴 하겠지만 공격자는 IRC서버에조차 직접적으로 접속하지 않을 가능성이 높습니다.

프록시(Proxy, 대리인)라고 불리는 다른 서버를 공유하여 자신의 신분을 완전히 바꿔 접속할수도 있습니다. 그리고 IRC서버를 찾았다고 해도 대부분이 관리가 제대로 이루어지지 않는 곳일 확률이 높습니다. 결과적으로 공격자를 찾기 위해서는 최소한 다음의 과정이 필요합니다.

좀비PC수거 → 명령을 전달받기 위해 어디로 접속하는지 분석 → 접속한 서버의 접속 로그 분석 → 공격자로 판단되는 사람의 IP추적

이게 추적할수 있는 완벽한 방법이 아니고 생각해 볼수 있는 가장 단순한 과정입니다. 접속한 서버의 로그를 볼수 있는 방법은 없다고 봐야 합니다 .서버가 한국에 있을리가 없기 떄문입니다. 공격자를 찾아내는건 불가능에 가깝습니다. 찾을 수 있다면 왜 DDOS 공격자 체포관련 뉴스가 안나올까요.

만약 찾아낼 수 있다면, 돈을 목적으로 DDOS공격을 하여 메일등으로 돈을 요구해올때 그걸 추적할수는 있을지 모르겠네요.

말이 너무 많이 돌았습니다. 45자 암호키가 똑같다고 하는데 이게 무슨 말인지 모르겠습니다. DDOS공격에 암호키라니…아무래도 어려운말 써봤자 국민들이 모를꺼라 생각하고 두리뭉실하게 쓴거 같습니다만 공격 코드의 일부분이 동일하다고 말하고 싶은게 아닐까 생각됩니다.

프로그램이라는것은 소스코드를 컴파일이라는 과정을 통해 만들어집니다. 이 컴파일이라는것은 개발자가 아무렇게나 개발해도 나름의 최적화(Optimize)를 통해 자기가 소스코드를 정리하여 실행파일로 만들어내게 됩니다. 결과적으로 프로그램의 코드만 봤을때 비슷한 부분은 얼마든지 많이 생길 수 있습니다. 설마 암호키/공개키를 이야기 하려는것은 아니었겠죠. 그럼 더욱 바보인증일테니.

농협 해킹 ‘소스코드’ 7.7 디도스와 상당부분 일치-동아일보

오히려 저런 웃기는 이야기도 있습니다. 소스코드가 상당부분 일치 한다니…분명히 저런 발표를 하는 사람들은 제가 범접하기도 힘든 초고수들일텐디 미칠꺼 같습니다.소스코드는 실행되지 않습니다. 말그대로 설계도 같은거죠. 사람들이 건축 평면도에 거주하지 않습니다. 그걸로 만들어진 건축물에 거주하겠죠. 물론 인터프리터언어같은 예외가 있습니다만 지금 상황에선 예외일꺼 같고요.

차라리 위의 정황에서 봤을떄는 한국 IBM의 직원 한모씨의 노트북이 바이러스 백신조차 안깔려있었다는 이야기를 볼때 작년 DDOS대란때에 좀비PC였었다고 보는게 맞다고 봅니다. DDOS는 단순한 공격자의 명령만을 수행하지 서버작업과 같은 상호작용(Interaction)을 하는 작업은 어려움이 있을 수 있습니다. 이것을 DDOS와 자꾸 엮을려고 하는건 억지라고 생각합니다. DDOS는 DDOS였고 이건 백도어든지 키로그던지 다른 해킹 사건이라는것이죠.

2. 삭제파일 목록, 중복기재 실수까지 판박이

이건 또 무슨 깜짝 놀랄 이야기인가요. 농협의 서버는 잘 알려져 있듯이 유닉스들로 구성되어있다고 합니다. hwp파일을 삭제하던 말던 전혀 다른 이야기를 들고 나왔다고 밖에 생각하기 힘듭니다. 차라리 위에서 언급했던 작년 DDOS대란 당시에 좀비PC였을것이라는 것이죠.

악성코드가 81개나 발견되었다니 IBM직원이 어떻게 저럴수 있을까 싶네요. IT업계 사람의 PC가 악성코드가 저렇게 많이 발견되었다는것은 말그대로 아예 관리를 안했다고 보는게 맞을것 같습니다. DDOS 좀비PC여도 전혀 이상하지 않습니다.

3. 北사용 ‘좀비PC 통신용 고유번호’가 노트북에

이것은 정말 저를 화나게 하는 이야기입니다. 여기서 통신용 고유번호라는 마치 그럴싸한 이야기를 했지만 기사의 내용을 보면 맥주소(Mac Address)를 말하는 것입니다. PC의 IP주소라는 것은 유동적인 주소입니다. 언제든지 바뀔 수 있는 주소이지만 맥주소는 변경되지 않는 자신의 주민등록번호와도 같은 주소값 입니다.

하지만 문제가 이 맥주소는 통신과정에서 변경됩니다. 이게 무슨말이냐면 다음의 그림을 보여드리겠습니다.
사용자 삽입 이미지네트워크는 매우 복잡하게 얽혀있습니다. 여기서 통신을 제대로 하기 위해서는 통신 패킷이 어디로 흘러갈지를 중간중간에 있는 Router(동그란 케잌모양)라는 것이 방향을 알려주어 통신하게 됩니다.

그런데 이 과정에서 발송자의 맥주소는 라우터의 맥주소로 변경이 됩니다. 같은 회사 같은 사무실 같은 부서의 옆사람을 내 PC로 공격하는게 아닌이상 내 맥주소를 상대방이 알 수 있다는것은 불가능에 가깝습니다. 이건 네트워크에서도 어려운 이야기가 아닙니다. 시스코 자격증중에 가장 낮은 CCNA에서도 나오는 이야기입니다.

그냥 저런 말을 한 의도가 궁금합니다. DDOS공격에서 IP는 의미있는 데이터가 될수 없고(좀비PC census라면 모를까) 맥주소 이야기는 꺼내는것 자체가 웃기는 이야기입니다.

농협 해킹이 북한 소행이라는 검찰 발표에 전문가들은 갸웃-조선일보

차라리 이 기사가 좀 더 사실을 언급하고 있습니다. IP가 의미가 없다는 이야기와 맥주소가 변경된다는 것에 대한 언급이 있네요.

“농협 해킹 북한 소행이라면 디도스 때 지목된 IP 또 썼겠나”-경향신문

경향신문은 해당 IP를 또 썼겠냐는 이야기를 들고 있습니다. 물론 DDOS라는것은 감염할 좀비PC를 공격자가 컨트롤할수 있는게 아닙니다. 얼마든지 그PC가 그 PC이고 백신으로 치료했지만 또 감염될 수 있습니다. 자꾸 체신성 IP가 언급되는데 제 생각엔 체신성 IP가 나왔다면 더더욱 북한의 짓이 아니라는 결론이 나와버립니다.

처음에 보여드린 그림에서 알 수 있듯이 좀비PC들은 감염당한 피해자입니다. 공격을 당하는 입장에서는 죽어도 공격자의 IP를 알 방법이 없습니다. 마치 암살자집단에 암살 의뢰를 한후에 암살현장에 자신도 참여하여 같이 칼부림을 하는 꼴입니다.

물론 PC관리를 너무 못해서…공격하는 팀에서조차 자신의 PC가 감염되었을수도 있습니다. DDOS에 공격해 오는 IP들도 피해자입니다.

공격 명령 서버중 1개 북한서 사용…노트북에 악성코드 심어 좀비피시화-한겨레

여기 또 답답한 기사가 있습니다. 정확히는 답답한 검찰의 발표겠지만요. 7개월동안 집중적으로 정보를 캐내로 81개나 되는 공격용 프로그램을 설치했다…제대로 관리안하는 PC들에서는 몇천개의 바이러스에 감염되는것이 일도 아닙니다. IP이야기 역시도 북한의 IP가 나오는것이 북한이 공격자라는 근거가 될수가 없습니다.

서버 관리자로써 결론을 지어보자면.

리눅스를 사랑하는 사람으로써 복잡한 마무리를 지어보겠습니다. 보통의 서비스는 적어도 WEB – WAS – DB와 같이 복잡한 구조로 서비스가 구성됩니다. 더군다나 IT(Information Technology)에서는 말 그대로 정보를 가장 중요시 합니다. DB와 같은 정보를 보관하는 서버들을 동시다발적으로 날릴수 있는것은 한번 해보라고 해도 어려운 일입니다. 정말 내부 구조를 잘 이해하고 있는 사람의 소행이라는 추정이 맞다고 생각합니다.

도대체 어떤 명령(rm.dd?)이 시스템을 복구도 못할정도로 한방에 날리는지 정말 궁금합니다만, 더군다나 동시다발적으로 다수의 서버를 날리는걸 어떻게 한건지 저도 궁금하지만 검찰의 발표는 마치 우리나라에는 보안 전문가는 없는게 아닌게 생각하게 만드는 발표입니다.

검찰분들은 굉장히 똑똑하신 분들입니다. 저랑은 비교도 할 수 없죠. 그래서 굉장히 무섭습니다. 일부러 몇치 앞을 내다보고 저런 발표를 한것일테니깐요…의중을 읽을수조차 없네요.

대표적인 미국의 SNS : 페이스북(facebook) 소스코드 유출!

사용자 삽입 이미지미국의 SNS 시장을 이끌고 있는 서비스로는 Myspace가 있습니다. 그 뒤로 Facebook이 있지요.

Facebook은 대학교의 인맥형성을 주제로 생겨났다가 가입자체를 공개하면서 엄청난 성장세를 보여왔습니다.

위키피디아 의 자료를 보시면 알수 있겠지만 현재 Facebook의 가입자수는 3500만명에 달합니다.

Facebook의 경우 가입자수는 한달이내로 로그인한적이 있는 Active 유저만을 계산한다고 하는군요.

위에 싸이월드도 있군요. 1500만명입니다.

저도 Facebook을 사용하고 있습니다만 친구들의 세세한 업데이트 현황까지 보여주며 또한 네트워크라는것을 통해 인맥을 형성하는데 강력한 기능을 제공한다는 점이 대단하다고 생각되어집니다.

또한 저같은 개발자들이 눈에 띄게 보게 되는것이 강력한 위젯 개발 기능을 제공하는데요.

이런 대형 서비스에서 서비스의 핵심코드까지 접근할 수 있는 강력한 기능을 일반 개발자들에게 준다는것이 저로서는 조금 이해하기 힘든 부분입니다만, 개발자의 입장에서는 정말 멋진 서비스임에 틀림이 없습니다.

다만 문제가, 사용자가 원하는 쿼리문까지 작성하여 디비에 직접 엑세스 할수 있고, 모든 자원에 접근 할수 있다는점이 문제가 아닐까 생각되는데요.

Developers에 등록을 하고, API Key를 받아야만 개발을 할수가 있습니다. 하지만 악의적인 마음을 품고 있는 사람에게 그것도 무용지물인 제제안이 될것입니다.

아니나 다를까, 해킹을 당했다고 합니다. TechCrunch에는 Facebook Source Code Leaked 라는 제목의 글이 올라왔습니다.

공식 확인된것은 아니지만 유출된 코드가 Facebook의 코드가 맞는듯 합니다.

코드를 유출한 사람은 blogspot에 facebooksecrets라는 계정을 만들고 index.php 소스내용을 올려둔 상태입니다.

과연 어떤 경로로 소스가 유출되었을까요? 솔직히 위젯 개발에서 PHP의 왠만한 함수를 접근할수 있다면 얼마든지 소스를 유출할수 있다고 생각되어집니다만, 확인해볼만큼 간이 크진 않습니다^^

확실히 생각해 볼수록 PHP로 제작된 서비스에서는 사용자가 PHP코드에 접근할수 있어서는 안되는 것이 맞다고 생각되어집니다.

[#M_ more.. | less.. |Index.php

[code type=php]include_once $_SERVER[‘PHP_ROOT’].’/html/init.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/home.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/requests.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/feed/newsfeed.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/poke.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/share.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/orientation.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/feed/newsfeed.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/mobile/register.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/forms_lib.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/contact_importer/contact_importer.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/feed/util.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/hiding_prefs.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/abtesting.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/friends.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/statusupdates.php’;

// lib/display/feed.php has to be declared here for scope issues.
// This keeps display/feed.php cleaner and easier to understand.
include_once $_SERVER[‘PHP_ROOT’].’/lib/display/feed.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/monetization_box.php’;

// require login
$user = require_login();
print_time(‘require_login’);
param_request(array( ‘react’ => $PARAM_EXISTS));

// Check and fix broken emails
// LN – disabling due to excessive can_see dirties and sets when enabled.
//check_and_fix_broken_emails($user);

// migrate AIM screenname from profile to screenname table if needed
migrate_screenname ($user);

// homepage announcement variables
$HIDE_ANNOUNCEMENT_BIT = get_site_variable(‘HIDE_ANNOUNCEMENT_BIT’);
$HIDE_INTRO_BITMASK = get_site_variable(‘HIDE_INTRO_BITMASK’);

// redirects
if (is_sponsor_user()) {
redirect(‘bizhome.php’, ‘www’);
}

include_once $_SERVER[‘PHP_ROOT’].’/lib/mesg.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/invitetool.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/grammar.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/securityq.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/events.php’;
include_once $_SERVER[‘PHP_ROOT’].’/lib/rooster/stories.php’;

// todo: password confirmation redirects here (from html/reset.php),
// do we want a confirmation message?

param_get_slashed(array(
‘feeduser’ => $PARAM_INT, //debug: gets feed for user here
‘err’ => $PARAM_STRING, // returning from a failed entry on an orientation form
‘error’ => $PARAM_STRING, // an error can also be here because the profile photo upload code is crazy
‘ret’ => $PARAM_INT,
‘success’ => $PARAM_INT, // successful profile picture upload
‘jn’ => $PARAM_INT, // joined a network for orientation
‘np’ => $PARAM_INT, // network pending (for work/address network)
‘me’ => $PARAM_STRING, // mobile error
‘mr’ => $PARAM_EXISTS, // force mobile reg view
‘mobile’ => $PARAM_EXISTS, // mobile confirmation code sent
‘jif’ => $PARAM_EXISTS, // just imported friends
‘ied’ => $PARAM_STRING, // import email domain
‘o’ => $PARAM_EXISTS, // first time orientation, passed on confirm
‘verified’ => $PARAM_EXISTS)); // verified mobile phone

param_post(array(
‘leave_orientation’ => $PARAM_EXISTS,
‘show_orientation’ => $PARAM_INT, // show an orientation step
‘hide_orientation’ => $PARAM_INT)); // skip an orientation step

// homepage actions
if ($req_react && validate_expiring_hash($req_react, $GLOBALS[‘url_md5key’])) {
$show_reactivated_message = true;
} else {
$show_reactivated_message = false;
}
tpl_set(‘show_reactivated_message’, $show_reactivated_message);

// upcoming events
events_check_future_events($user); // make sure big tunas haven’t moved around
$upcoming_events = events_get_imminent_for_user($user);

// this is all stuff that can be fetched together!
$upcoming_events_short = array();
obj_multiget_short(array_keys($upcoming_events), true, $upcoming_events_short);
$new_pokes = 0;
//only get the next N pokes for display
//where N is set in the dbget to avoid caching issues
$poke_stats = get_num_pokes($user);
get_next_pokes($user, true, $new_pokes);
$poke_count = $poke_stats[‘unseen’];

$targeted_data = array();
home_get_cache_targeted_data($user, true, $targeted_data);
$announcement_data = array();
home_get_cache_announcement_data($user, true, $announcement_data);
$orientation = 0;
orientation_get_status($user, true, $orientation);
$short_profile = array();
profile_get_short($user, true, $short_profile);
// pure priming stuff
privacy_get_network_settings($user, true);
$presence = array();
mobile_get_presence_data($user, true, $presence);
feedback_get_event_weights($user, true);
// Determine if we want to display the feed intro message
$intro_settings = 0;
user_get_hide_intro_bitmask($user, true, $intro_settings);
$user_friend_finder = true;
contact_importer_get_used_friend_finder($user, true, $used_friend_finder);
$all_requests = requests_get_cache_data($user);
// FIXME?: is it sub-optimal to call this both in requests_get_cache_data and here?
$friends_status = statusupdates_get_recent($user, null, 3);
memcache_dispatch(); // populate cache data

// Merman’s Admin profile always links to the Merman’s home
if (user_has_obj_attached($user)) {
redirect(‘mhome.php’, ‘www’);
}

if (is_array($upcoming_events)) {
foreach ($upcoming_events as $event_id => $data) {
$upcoming_events[$event_id][‘name’] = txt_set($upcoming_events_short[$event_id][‘name’]);
}
}

tpl_set(‘upcoming_events’ , $upcoming_events);

// disabled account actions
$disabled_warning = ((IS_DEV_SITE || IS_QA_SITE) && is_disabled_user($user));
tpl_set(‘disabled_warning’, $disabled_warning);

// new pokes (no more messages here, they are in the top nav!)
if (!user_is_guest($user)) {
tpl_set(‘poke_count’ , $poke_count);
tpl_set(‘pokes’ , $new_pokes);
}

// get announcement computations
tpl_set(‘targeted_data’ , $targeted_data);
tpl_set(‘announcement_data’ , $announcement_data);

// birthday notifications
tpl_set(‘birthdays’ , $birthdays = user_get_birthday_notifications($user, $short_profile));
tpl_set(‘show_birthdays’ , $show_birthdays = (count($birthdays) || !$orientation));

// user info
tpl_set(‘first_name’ , user_get_first_name(txt_set($short_profile[‘id’])));
tpl_set(‘user’ , $user);

// decide if there are now any requests to show
$show_requests = false;
foreach ($all_requests as $request_category) {
if ($request_category) {
$show_requests = true;
break;
}
}
tpl_set(‘all_requests’, $show_requests ? $all_requests : null);

$permissions = privacy_get_reduced_network_permissions($user, $user);

// status
$user_info = array(‘user’ => $user,
‘firstname’ => user_get_first_name($user),
‘see_all’ => ‘/statusupdates/?ref=hp’,
‘profile_pic’ => make_profile_image_src_direct($user, ‘thumb’),
‘square_pic’ => make_profile_image_src_direct($user, ‘square’));

if (!empty($presence) && $presence[‘status_time’] > (time() – 60*60*24*7)) {
$status = array(‘message’ => txt_set($presence[‘status’]),
‘time’ => $presence[‘status_time’],
‘source’ => $presence[‘status_source’]);
} else {
$status = array(‘message’ => null, ‘time’ => null, ‘source’ => null);
}
tpl_set(‘user_info’, $user_info);

tpl_set(‘show_status’, $show_status = !$orientation);
tpl_set(‘status’, $status);
tpl_set(‘status_custom’, $status_custom = mobile_get_status_custom($user));
tpl_set(‘friends_status’, $friends_status);

// orientation
if ($orientation) {
if ($post_leave_orientation) {
orientation_update_status($user, $orientation, 2);
notification_notify_exit_orientation($user);
dirty_user($user);
redirect(‘home.php’);
} else if (orientation_eligible_exit(array(‘uid’=>$user)) == 2) {
orientation_update_status($user, $orientation, 1);
notification_notify_exit_orientation($user);
dirty_user($user);
redirect(‘home.php’);
}
}

// timezone – outside of stealth, update user’s timezone if necessary
$set_time = !user_is_alpha($user, ‘stealth’);
tpl_set(‘timezone_autoset’, $set_time );
if ($set_time) {
$daylight_savings = get_site_variable(‘DAYLIGHT_SAVINGS_ON’);
tpl_set(‘timezone’, $short_profile[‘timezone’] – ($daylight_savings ? 4 : 5) );
}

// set next step if we can
if (!$orientation) {
user_set_next_step($user, $short_profile);
}

// note: don’t make this an else with the above statement, because then no news feed stories will be fetched if they’re exiting orientation
if ($orientation) {
extract(orientation_get_const());

require_js(‘js/dynamic_dialog.js’);
require_js(‘js/suggest.js’);
require_js(‘js/typeahead_ns.js’);
require_js(‘js/suggest.js’);
require_js(‘js/editregion.js’);
require_js(‘js/orientation.js’);
require_css(‘css/typeahead.css’);
require_css(‘css/editor.css’);

if ($post_hide_orientation && $post_hide_orientation <= $ORIENTATION_MAX) {
$orientation[‘orientation_bitmask’] |= ($post_hide_orientation * $ORIENTATION_SKIPPED_MODIFIER);
orientation_update_status($user, $orientation);
} else if ($post_show_orientation && $post_show_orientation <= $ORIENTATION_MAX) {
$orientation[‘orientation_bitmask’] &= ~($post_show_orientation * $ORIENTATION_SKIPPED_MODIFIER);
orientation_update_status($user, $orientation);
}

$stories = orientation_get_stories($user, $orientation);
switch ($get_err) {
case $ORIENTATION_ERR_COLLEGE:
$temp = array(); // the affil_retval_msg needs some parameters won’t be used
$stories[$ORIENTATION_NETWORK][‘failed_college’]=affil_retval_msg($get_ret, $temp, $temp);
break;
case $ORIENTATION_ERR_CORP:
$temp = array();
// We special case the network not recognized error here, because affil_retval_msg is retarded.
$stories[$ORIENTATION_NETWORK][‘failed_corp’] = ($get_ret == 70) ? ‘The email you entered did not match any of our supported networks. ‘ .
‘Click here to see our supported list. ‘ .
‘Go here to suggest your network for the future.’
: affil_retval_msg($get_ret, $temp, $temp);
break;
}

// photo upload error
if ($get_error) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_PROFILE]][‘upload_error’] = pic_get_error_text($get_error);
}
// photo upload success
else if ($get_success == 1) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_PROFILE]][‘uploaded_pic’] = true;
// join network success
} else if ($get_jn) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_NETWORK]][‘joined’] = array(
‘id’ => $get_jn,
‘name’ => network_get_name($get_jn));
// network join pending
} else if ($get_np) {

$stories[$ORIENTATION_ORDER[$ORIENTATION_NETWORK]][‘join_pending’] = array(
‘id’ => $get_np,
’email’ => get_affil_email_conf($user, $get_np),
‘network’ => network_get_name($get_np));
// just imported friend confirmation
} else if ($get_jif) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_NETWORK]][‘just_imported_friends’] = true;
$stories[$ORIENTATION_ORDER[$ORIENTATION_NETWORK]][‘domain’] = $get_ied;
}

// Mobile web API params
if ($get_mobile) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_MOBILE]][‘sent_code’] = true;
$stories[$ORIENTATION_ORDER[$ORIENTATION_MOBILE]][‘view’] = ‘confirm’;
}
if ($get_verified) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_MOBILE]][‘verified’] = true;
}
if ($get_me) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_MOBILE]][‘error’] = $get_me;
}
if ($get_mr) {
$stories[$ORIENTATION_ORDER[$ORIENTATION_MOBILE]][‘view’] = ‘register’;
}

if (orientation_eligible_exit($orientation)) {
tpl_set(‘orientation_show_exit’, true);
}
tpl_set(‘orientation_stories’, $stories);

//if in orientation, we hide all feed intros (all 1’s in bitmask)
$intro_settings = -1;

}
tpl_set(‘orientation’, $orientation);

// Rooster Stories
if (!$orientation &&
((get_site_variable(‘ROOSTER_ENABLED’) == 2) ||
(get_site_variable(‘ROOSTER_DEV_ENABLED’) == 2))) {
$rooster_story_count = get_site_variable(‘ROOSTER_STORY_COUNT’);
if (!isset($rooster_story_count)) {
// Set default if something is wrong with the sitevar
$rooster_story_count = 2;
}
$rooster_stories = rooster_get_stories($user, $rooster_story_count, $log_omissions = true);
if (!empty($rooster_stories) && !empty($rooster_stories[‘stories’])) {
// Do page-view level logging here
foreach($rooster_stories[‘stories’] as $story) {
rooster_log_action($user, $story, ROOSTER_LOG_ACTION_VIEW);
}
tpl_set(‘rooster_stories’, $rooster_stories);
}
}

// set the variables for the home announcement code
$hide_announcement_tpl = ($intro_settings | $HIDE_INTRO_BITMASK) & $HIDE_ANNOUNCEMENT_BIT;
// if on qa/dev site, special rules
$HIDE_INTRO_ON_DEV = get_site_variable(‘HIDE_INTRO_ON_DEV’);
if ((IS_QA_SITE || IS_DEV_SITE) && !$HIDE_INTRO_ON_DEV) {
$hide_announcement_tpl = 0;
}

tpl_set(‘hide_announcement’, $hide_announcement_tpl);
if($is_candidate = is_candidate_user($user)) {
tpl_set(‘hide_announcement’, false);
}
$home_announcement_tpl = !$hide_announcement_tpl || $is_candidate ? home_get_announcement_info($user) : 0;
tpl_set(‘home_announcement’, $home_announcement_tpl);
tpl_set(‘hide_announcement_bit’, $HIDE_ANNOUNCEMENT_BIT);

$show_friend_finder = !$orientation && contact_importer_enabled($user) && !user_get_hiding_pref($user, ‘home_friend_finder’);
tpl_set(‘show_friend_finder’, $show_friend_finder);
if ($show_friend_finder && (user_get_friend_count($user) > 20)) {
tpl_set(‘friend_finder_hide_options’, array(‘text’=>’close’,
‘onclick’=>”return clearFriendFinder()”));
} else {
tpl_set(‘friend_finder_hide_options’, null);
}

$account_info = user_get_account_info($user);
$account_create_time = $account_info[‘time’];

tpl_set(‘show_friend_finder_top’,
!$used_friend_finder);

tpl_set(‘user’, $user);

// MONETIZATION BOX
$minimize_monetization_box = user_get_hiding_pref($user, ‘home_monetization’);
$show_monetization_box = (!$orientation &&
get_site_variable(‘HOMEPAGE_MONETIZATION_BOX’));
tpl_set(‘show_monetization_box’, $show_monetization_box);
tpl_set(‘minimize_monetization_box’, $minimize_monetization_box);

if ($show_monetization_box) {
$monetization_box_data = monetization_box_user_get_data($user);
txt_set(‘monetization_box_data’, $monetization_box_data);
}

// ORIENTATION
if ($orientation) {
$network_ids = id_get_networks($user);
$network_names = multiget_network_name($network_ids);
$in_corp_network = in_array($GLOBALS[‘TYPE_CORP’], array_map(‘extract_network_type’, $network_ids));
$show_corp_search = $in_corp_network ||
get_age(user_get_basic_info_attr($user, ‘birthday’)) >= 21;
$pending_hs = is_hs_pending_user($user);
$hs_id = null;
$hs_name = null;
if ($pending_hs) {
foreach (id_get_pending_networks($user) as $network) {
if (extract_network_type($network[‘network_key’]) == $GLOBALS[‘TYPE_HS’]) {
$hs_id = $network[‘network_key’];
$hs_name = network_get_name($hs_id);
break;
}
}
}
//$orientation_people = orientation_get_friend_and_inviter_ids($user);
$orientation_people = array(‘friends’ => user_get_all_friends($user),
‘pending’ => array_keys(user_get_friend_requests($user)),
‘inviters’=> array(), // wc: don’t show inviters for now
);
$orientation_info = array_merge($orientation_people,
array(‘network_names’ => $network_names,
‘show_corp_search’ => $show_corp_search,
‘pending_hs’ => array(‘hs_id’=>$hs_id,
‘hs_name’=>$hs_name),
‘user’ => $user,
));
tpl_set(‘orientation_info’, $orientation_info);

tpl_set(‘simple_orientation_first_login’, $get_o); // unused right now
}

// Roughly determine page length for ads
// first, try page length using right-hand panel
$ads_page_length_data = 3 + // 3 for profile pic + next step
($show_friend_finder ? 1 : 0) +
($show_status ? ($status_custom ? count($friends_status) : 0) : 0) +
($show_monetization_box ? 1 : 0) +
($show_birthdays ? count($birthdays) : 0) +
count($new_pokes);

// page length using feed stories
if ($orientation) {
$ads_page_length_data = max($ads_page_length_data, count($stories) * 5);
}
tpl_set(‘ads_page_length_data’, $ads_page_length_data);

$feed_stories = null;
if (!$orientation) { // if they’re not in orientation they get other cool stuff
// ad_insert: the ad type to try to insert for the user
// (0 if we don’t want to try an insert)
$ad_insert = get_site_variable(‘FEED_ADS_ENABLE_INSERTS’);

$feed_off = false;

if (check_super($user) && $get_feeduser){
$feed_stories = user_get_displayable_stories($get_feeduser, 0, null, $ad_insert);
} else if (can_see($user, $user, ‘feed’)) {
$feed_stories = user_get_displayable_stories($user, 0, null, $ad_insert);
} else {
$feed_off = true;
}

// Friend’s Feed Selector – Requires dev.php constant
if (is_friendfeed_user($user)) {
$friendfeed = array();
$friendfeed[‘feeduser’] = $get_feeduser;
$friendfeed[‘feeduser_name’] = user_get_name($get_feeduser);
$friendfeed[‘friends’] = user_get_all_friends($user);
tpl_set(‘friendfeed’, $friendfeed);
}

$feed_stories = feed_adjust_timezone($user, $feed_stories);

tpl_set(‘feed_off’, $feed_off ? redirect(‘privacy.php?view=feeds’, null, false) : false);
}
tpl_set(‘feed_stories’, $feed_stories);

render_template($_SERVER[‘PHP_ROOT’].’/html/home.phpt’);[/code]
_M#]